@inneranimalmedia/agentsam-sdk 2.6.0 → 2.6.1
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/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
- package/docs/RELEASES.md +7 -7
- package/docs/SOURCE_ARCHITECTURE.md +58 -0
- package/docs/TEST_TIERS.md +26 -0
- package/migrations/runtime/0001_cli_runtime.sql +298 -0
- package/package.json +28 -7
- package/packages/agentsam-repository/README.md +15 -0
- package/packages/agentsam-repository/package.json +25 -0
- package/packages/agentsam-repository/src/contracts.js +113 -0
- package/packages/agentsam-repository/src/index.js +3 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
- package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
- package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
- package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
- package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
- package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
- package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
- package/packages/identity/package.json +1 -1
- package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
- package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
- package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
- package/protocol/repository/repository-contract.schema.json +24 -0
- package/protocol/repository/repository-dependency.schema.json +24 -0
- package/protocol/repository/repository-identity.schema.json +17 -0
- package/protocol/rpc/v1/common.proto +16 -0
- package/protocol/rpc/v1/errors.proto +35 -0
- package/protocol/rpc/v1/knowledge.proto +77 -0
- package/services/knowledge/package-lock.json +333 -0
- package/services/knowledge/package.json +5 -1
- package/src/agent/responses-runner.js +63 -35
- package/src/capabilities/repository-snapshot.js +3 -3
- package/src/cli.js +23 -6
- package/src/commands/context-economics.js +17 -2
- package/src/commands/context.js +1 -1
- package/src/commands/db.js +20 -3
- package/src/commands/env.js +90 -0
- package/src/commands/knowledge.js +12 -4
- package/src/commands/merkle-persist.js +30 -11
- package/src/commands/merkle.js +1 -1
- package/src/commands/models.js +123 -65
- package/src/commands/ollama.js +26 -0
- package/src/commands/preferences.js +53 -26
- package/src/commands/shell.js +236 -48
- package/src/errors/contract.js +236 -0
- package/src/errors/index.js +14 -0
- package/src/index.js +13 -1
- package/src/knowledge/service/auth.js +13 -0
- package/src/knowledge/service/grpc-client.js +115 -0
- package/src/knowledge/service/grpc-codec.js +237 -0
- package/src/knowledge/service/grpc-server.js +83 -0
- package/src/knowledge/service/job-engine.js +248 -0
- package/src/knowledge/service/server.js +87 -135
- package/src/knowledge/source.js +1 -1
- package/src/lib/cli-preferences.js +31 -4
- package/src/lib/deploy-receipt/index.js +2 -2
- package/src/lib/knowledge-docker.js +6 -3
- package/src/lib/local-sessions.js +23 -2
- package/src/lib/local-status.js +1 -1
- package/src/lib/project-config.js +1 -1
- package/src/lib/provider-credentials.js +105 -5
- package/src/lib/slash-commands.js +4 -3
- package/src/local/migrations.js +93 -0
- package/src/local/runtime-store.js +141 -0
- package/src/local/sqlite.js +2 -0
- package/src/local-pty/server.js +113 -51
- package/src/models/discovery.js +292 -0
- package/src/providers/anthropic-messages.js +192 -0
- package/src/providers/cloudflare-chat.js +183 -0
- package/src/providers/factory.js +69 -0
- package/src/providers/gemini-generate-content.js +208 -0
- package/src/providers/index.js +5 -0
- package/src/providers/ollama-chat.js +148 -0
- package/src/providers/openai-responses.js +226 -75
- package/src/repository/index.js +14 -2
- package/src/rpc/generated/common_grpc_pb.js +1 -0
- package/src/rpc/generated/common_pb.js +536 -0
- package/src/rpc/generated/errors_grpc_pb.js +1 -0
- package/src/rpc/generated/errors_pb.js +482 -0
- package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
- package/src/rpc/generated/knowledge_pb.js +2168 -0
- package/src/rpc/generated/package.json +3 -0
- package/src/security/trust-boundary.js +2 -2
- package/src/telemetry/events.js +4 -1
- package/src/ui/cli/activity.js +76 -0
- package/src/ui/cli/compaction.js +15 -0
- package/src/ui/cli/footer.js +39 -0
- package/src/ui/cli/help.js +192 -0
- package/src/ui/cli/plan.js +20 -0
- package/src/ui/cli/runtime-events.js +110 -0
- package/src/ui/cli/waiting.js +16 -0
- package/src/ui/merkle/render.js +1 -1
- package/test/cli/preferences-runtime.test.mjs +11 -0
- package/test/cli/runtime-ui.test.mjs +74 -0
- package/test/error-diagnostics.test.mjs +57 -1
- package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
- package/test/integration/cli-help.test.mjs +37 -0
- package/test/integration/knowledge-rpc.test.mjs +112 -0
- package/test/integration/merkle-cli.test.mjs +61 -0
- package/test/integration/merkle-persistence-identity.test.mjs +48 -0
- package/test/integration/provider-env-cli.test.mjs +49 -0
- package/test/integration/provider-factory.test.mjs +197 -0
- package/test/integration/repository-company-graph.test.mjs +90 -0
- package/test/integration/runtime-migrations.test.mjs +82 -0
- package/test/knowledge-service.test.mjs +5 -0
- package/test/knowledge.test.mjs +16 -0
- package/test/live/terminal-transport.live.test.mjs +24 -0
- package/test/local-sessions.test.mjs +7 -1
- package/test/models.test.mjs +101 -4
- package/test/ollama.test.mjs +21 -0
- package/test/portable-context.test.mjs +1 -1
- package/test/provider-credentials.test.mjs +45 -1
- package/test/release-hygiene.test.mjs +13 -5
- package/test/responses-runner.test.mjs +3 -1
- package/test/shell.test.mjs +50 -8
- package/test/terminal/local-pty.mock.test.mjs +151 -0
- /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
|
@@ -23,9 +23,11 @@ function userMessage(text) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function modelBudget(record) {
|
|
26
|
+
const windowTokens = Number(record?.context_window);
|
|
27
|
+
if (!Number.isFinite(windowTokens) || windowTokens <= 0) return null;
|
|
26
28
|
const policy = record.context_policy || {};
|
|
27
29
|
return createContextBudget({
|
|
28
|
-
windowTokens
|
|
30
|
+
windowTokens,
|
|
29
31
|
targetInputTokens: policy.target_input_tokens,
|
|
30
32
|
compactAtTokens: policy.compact_at_tokens,
|
|
31
33
|
interveneAtTokens: policy.intervene_at_tokens,
|
|
@@ -102,6 +104,7 @@ function boundedToolOutput(value, callId, maxChars) {
|
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
function toolResultCharBudget(activeTokens, budget) {
|
|
107
|
+
if (!budget) return 48_000;
|
|
105
108
|
if (!Number.isFinite(activeTokens) || activeTokens < 0) return budget.maxToolResultChars;
|
|
106
109
|
const reserveTokens = 4_000;
|
|
107
110
|
const headroomTokens = Math.max(256, budget.maxNormalInputTokens - Math.ceil(activeTokens) - reserveTokens);
|
|
@@ -110,12 +113,13 @@ function toolResultCharBudget(activeTokens, budget) {
|
|
|
110
113
|
|
|
111
114
|
function projectedInputTokens({ instructions, input, toolSurface, priorActiveTokens, budget }) {
|
|
112
115
|
const inputChars = typeof input === 'string' ? input.length : JSON.stringify(input ?? '').length;
|
|
113
|
-
const
|
|
116
|
+
const charsPerToken = budget?.charsPerToken || 4;
|
|
117
|
+
const newTokens = estimateContextTokens(String(instructions || '').length + inputChars + toolSurface.receipt.hydrated_schema_chars, charsPerToken);
|
|
114
118
|
return Math.max(newTokens, Number.isFinite(priorActiveTokens) ? Math.ceil(priorActiveTokens) + newTokens : newTokens);
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
function assertEconomicPreflight(projectedTokens, budget, allowOverride) {
|
|
118
|
-
if (allowOverride) return;
|
|
122
|
+
if (!budget || allowOverride) return;
|
|
119
123
|
if (budget.pricingThresholdTokens != null && projectedTokens > budget.pricingThresholdTokens) {
|
|
120
124
|
throw new Error(`context_preflight_pricing_threshold:${projectedTokens}>${budget.pricingThresholdTokens}`);
|
|
121
125
|
}
|
|
@@ -130,46 +134,56 @@ export async function runResponsesAgent(options = {}) {
|
|
|
130
134
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
131
135
|
const objective = clean(options.prompt);
|
|
132
136
|
if (!objective) throw new TypeError('prompt is required');
|
|
133
|
-
const record = getModelRecord(options.model);
|
|
137
|
+
const record = options.modelRecord || getModelRecord(options.model);
|
|
134
138
|
if (!record) throw new RangeError(`unknown model: ${options.model}`);
|
|
135
|
-
const reasoningEffort = clean(options.reasoningEffort || '
|
|
139
|
+
const reasoningEffort = clean(options.reasoningEffort || 'auto');
|
|
136
140
|
const serviceTier = clean(options.serviceTier || 'default');
|
|
137
141
|
const budget = modelBudget(record);
|
|
138
|
-
const instructionSet = options.instructions == null ? compileAgentInstructions(cwd, { maxChars: budget
|
|
142
|
+
const instructionSet = options.instructions == null ? compileAgentInstructions(cwd, { maxChars: budget?.maxSystemChars || 48_000 }) : null;
|
|
139
143
|
const instructions = options.instructions == null ? instructionSet.content : String(options.instructions);
|
|
140
144
|
const toolSurface = buildAgentToolSurface(options.capabilityAdapter, objective, options);
|
|
141
145
|
const emit = options.emit;
|
|
142
146
|
const runId = options.runId;
|
|
143
147
|
event(emit, 'tool.search', toolSurface.receipt, runId);
|
|
144
148
|
|
|
145
|
-
let previousResponseId = clean(options.previousResponseId) || null;
|
|
149
|
+
let previousResponseId = clean(options.previousResponseId || options.previousProviderState?.previous_response_id) || null;
|
|
150
|
+
let providerState = options.previousProviderState && typeof options.previousProviderState === 'object'
|
|
151
|
+
? structuredClone(options.previousProviderState)
|
|
152
|
+
: previousResponseId ? { previous_response_id: previousResponseId } : null;
|
|
146
153
|
let priorActiveTokens = options.previousUsageSnapshot?.current_context?.input_tokens;
|
|
147
154
|
let input = objective;
|
|
148
155
|
let compacted = null;
|
|
149
156
|
let projected = projectedInputTokens({ instructions, input, toolSurface, priorActiveTokens, budget });
|
|
150
157
|
|
|
151
|
-
if (projected >= budget.compactAtTokens &&
|
|
158
|
+
if (budget && projected >= budget.compactAtTokens && providerState && options.autoCompact !== false && typeof provider.compact === 'function') {
|
|
152
159
|
compacted = await provider.compact({
|
|
153
160
|
model: record.provider_model_id,
|
|
161
|
+
modelRecord: record,
|
|
154
162
|
previousResponseId,
|
|
163
|
+
providerState,
|
|
155
164
|
instructions,
|
|
156
165
|
promptCacheKey: options.promptCacheKey,
|
|
166
|
+
tokensBefore: Number.isFinite(priorActiveTokens) ? priorActiveTokens : projected,
|
|
157
167
|
emit,
|
|
158
168
|
runId,
|
|
159
169
|
});
|
|
160
|
-
input = [...
|
|
161
|
-
|
|
170
|
+
if (Array.isArray(compacted.output) && compacted.output.length) input = [...compacted.output, userMessage(objective)];
|
|
171
|
+
providerState = compacted.provider_state || null;
|
|
172
|
+
previousResponseId = clean(providerState?.previous_response_id) || null;
|
|
162
173
|
priorActiveTokens = null;
|
|
163
174
|
projected = projectedInputTokens({ instructions, input, toolSurface, priorActiveTokens, budget });
|
|
164
175
|
}
|
|
165
176
|
|
|
166
177
|
assertEconomicPreflight(projected, budget, options.allowEconomicOverride === true);
|
|
167
|
-
const pressure = assessContextUsage(projected, budget);
|
|
178
|
+
const pressure = budget ? assessContextUsage(projected, budget) : null;
|
|
179
|
+
const declaredMaxOutput = Number(record.max_output_tokens);
|
|
168
180
|
const maxOutputTokens = Number.isInteger(options.maxOutputTokens) && options.maxOutputTokens > 0
|
|
169
|
-
? Math.min(options.maxOutputTokens,
|
|
170
|
-
: Math.min(32_768,
|
|
171
|
-
const projectedCost =
|
|
172
|
-
|
|
181
|
+
? (Number.isFinite(declaredMaxOutput) && declaredMaxOutput > 0 ? Math.min(options.maxOutputTokens, declaredMaxOutput) : options.maxOutputTokens)
|
|
182
|
+
: (Number.isFinite(declaredMaxOutput) && declaredMaxOutput > 0 ? Math.min(32_768, declaredMaxOutput) : 16_384);
|
|
183
|
+
const projectedCost = record.pricing
|
|
184
|
+
? calculateModelCost(record, { input_tokens: projected, output_tokens: maxOutputTokens }, { serviceTier })
|
|
185
|
+
: null;
|
|
186
|
+
if (Number.isFinite(options.maxCallCostUsd) && projectedCost && projectedCost.total_usd > options.maxCallCostUsd) {
|
|
173
187
|
throw new Error(`projected_call_cost_exceeds_budget:${projectedCost.total_usd.toFixed(6)}>${Number(options.maxCallCostUsd).toFixed(6)}`);
|
|
174
188
|
}
|
|
175
189
|
const preflight = Object.freeze({
|
|
@@ -178,9 +192,9 @@ export async function runResponsesAgent(options = {}) {
|
|
|
178
192
|
service_tier: serviceTier,
|
|
179
193
|
estimated_input_tokens: projected,
|
|
180
194
|
max_output_tokens: maxOutputTokens,
|
|
181
|
-
projected_max_call_cost_usd: projectedCost
|
|
182
|
-
pricing_threshold_tokens: budget
|
|
183
|
-
tokens_until_pricing_threshold: pressure
|
|
195
|
+
projected_max_call_cost_usd: projectedCost?.total_usd ?? null,
|
|
196
|
+
pricing_threshold_tokens: budget?.pricingThresholdTokens ?? null,
|
|
197
|
+
tokens_until_pricing_threshold: pressure?.tokensUntilPricingThreshold ?? null,
|
|
184
198
|
compacted_before_turn: Boolean(compacted),
|
|
185
199
|
estimate_kind: 'local',
|
|
186
200
|
});
|
|
@@ -191,32 +205,40 @@ export async function runResponsesAgent(options = {}) {
|
|
|
191
205
|
event(emit, 'context.snapshot', {
|
|
192
206
|
estimate_kind: 'local',
|
|
193
207
|
estimated_input_tokens: projected,
|
|
194
|
-
window_tokens: budget
|
|
195
|
-
utilization_ratio: pressure
|
|
196
|
-
pricing_threshold_tokens: budget
|
|
197
|
-
tokens_until_pricing_threshold: pressure
|
|
198
|
-
pressure: pressure
|
|
199
|
-
projected_max_call_cost_usd: projectedCost
|
|
208
|
+
window_tokens: budget?.windowTokens ?? null,
|
|
209
|
+
utilization_ratio: pressure?.utilizationRatio ?? null,
|
|
210
|
+
pricing_threshold_tokens: budget?.pricingThresholdTokens ?? null,
|
|
211
|
+
tokens_until_pricing_threshold: pressure?.tokensUntilPricingThreshold ?? null,
|
|
212
|
+
pressure: pressure?.stage ?? 'unknown',
|
|
213
|
+
projected_max_call_cost_usd: projectedCost?.total_usd ?? null,
|
|
200
214
|
tool_surface: toolSurface.receipt,
|
|
201
215
|
}, runId);
|
|
202
216
|
|
|
203
217
|
let cumulativeUsage = options.cumulativeUsage || null;
|
|
204
218
|
let totalCostUsd = 0;
|
|
219
|
+
const costBreakdownUsd = { input: 0, cached_input: 0, cache_write: 0, output: 0 };
|
|
220
|
+
const accumulateCost = (cost) => {
|
|
221
|
+
totalCostUsd += Number(cost?.total_usd || 0);
|
|
222
|
+
for (const key of Object.keys(costBreakdownUsd)) costBreakdownUsd[key] += Number(cost?.components_usd?.[key] || 0);
|
|
223
|
+
};
|
|
205
224
|
let response = await provider.create({
|
|
206
225
|
model: record.provider_model_id,
|
|
226
|
+
modelRecord: record,
|
|
207
227
|
input,
|
|
208
228
|
instructions,
|
|
209
229
|
reasoningEffort,
|
|
210
230
|
serviceTier,
|
|
211
231
|
tools: toolSurface.tools,
|
|
212
232
|
previousResponseId: previousResponseId || undefined,
|
|
233
|
+
providerState,
|
|
213
234
|
maxOutputTokens,
|
|
214
235
|
promptCacheKey: options.promptCacheKey,
|
|
215
236
|
cumulativeUsage,
|
|
216
237
|
emit,
|
|
217
238
|
runId,
|
|
218
239
|
});
|
|
219
|
-
|
|
240
|
+
accumulateCost(response.cost);
|
|
241
|
+
providerState = response.provider_state || (response.response_id ? { previous_response_id: response.response_id } : providerState);
|
|
220
242
|
cumulativeUsage = response.usage_snapshot?.cumulative || cumulativeUsage;
|
|
221
243
|
|
|
222
244
|
const toolReceipts = [];
|
|
@@ -275,6 +297,8 @@ export async function runResponsesAgent(options = {}) {
|
|
|
275
297
|
response = await provider.continueWithToolOutputs({
|
|
276
298
|
model: record.provider_model_id,
|
|
277
299
|
previousResponseId: response.response_id,
|
|
300
|
+
providerState: response.provider_state || providerState,
|
|
301
|
+
modelRecord: record,
|
|
278
302
|
toolOutputs: outputs,
|
|
279
303
|
instructions,
|
|
280
304
|
reasoningEffort,
|
|
@@ -286,21 +310,22 @@ export async function runResponsesAgent(options = {}) {
|
|
|
286
310
|
emit,
|
|
287
311
|
runId,
|
|
288
312
|
});
|
|
289
|
-
|
|
313
|
+
accumulateCost(response.cost);
|
|
314
|
+
providerState = response.provider_state || (response.response_id ? { previous_response_id: response.response_id } : providerState);
|
|
290
315
|
cumulativeUsage = response.usage_snapshot?.cumulative || cumulativeUsage;
|
|
291
316
|
}
|
|
292
317
|
|
|
293
318
|
const active = response.usage_snapshot?.current_context?.input_tokens ?? 0;
|
|
294
|
-
const finalPressure = assessContextUsage(active, budget);
|
|
319
|
+
const finalPressure = budget ? assessContextUsage(active, budget) : null;
|
|
295
320
|
event(emit, 'context.snapshot', {
|
|
296
321
|
estimate_kind: 'provider',
|
|
297
322
|
active_input_tokens: active,
|
|
298
|
-
window_tokens: budget
|
|
299
|
-
utilization_ratio: finalPressure
|
|
300
|
-
pricing_threshold_tokens: budget
|
|
301
|
-
tokens_until_pricing_threshold: finalPressure
|
|
302
|
-
pressure: finalPressure
|
|
303
|
-
compact_before_next_turn: finalPressure
|
|
323
|
+
window_tokens: budget?.windowTokens ?? null,
|
|
324
|
+
utilization_ratio: finalPressure?.utilizationRatio ?? null,
|
|
325
|
+
pricing_threshold_tokens: budget?.pricingThresholdTokens ?? null,
|
|
326
|
+
tokens_until_pricing_threshold: finalPressure?.tokensUntilPricingThreshold ?? null,
|
|
327
|
+
pressure: finalPressure?.stage ?? 'unknown',
|
|
328
|
+
compact_before_next_turn: finalPressure?.shouldCompact ?? false,
|
|
304
329
|
}, runId);
|
|
305
330
|
|
|
306
331
|
return Object.freeze({
|
|
@@ -313,13 +338,16 @@ export async function runResponsesAgent(options = {}) {
|
|
|
313
338
|
usage_snapshot: response.usage_snapshot,
|
|
314
339
|
cumulative_usage: cumulativeUsage,
|
|
315
340
|
total_cost_usd: totalCostUsd,
|
|
341
|
+
cost_breakdown_usd: Object.freeze({ ...costBreakdownUsd }),
|
|
316
342
|
tool_surface: toolSurface.receipt,
|
|
317
343
|
tool_receipts: Object.freeze(toolReceipts),
|
|
318
344
|
compacted_before_turn: Boolean(compacted),
|
|
345
|
+
provider_state: providerState,
|
|
319
346
|
continuation: Object.freeze({
|
|
320
|
-
previous_response_id: response.response_id,
|
|
347
|
+
previous_response_id: response.response_id || providerState?.previous_response_id || null,
|
|
348
|
+
provider_state: providerState,
|
|
321
349
|
usage_snapshot: response.usage_snapshot,
|
|
322
|
-
compact_before_next_turn: finalPressure
|
|
350
|
+
compact_before_next_turn: finalPressure?.shouldCompact ?? false,
|
|
323
351
|
}),
|
|
324
352
|
});
|
|
325
353
|
}
|
|
@@ -4,10 +4,10 @@ import { execFile } from 'node:child_process';
|
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
|
-
import { resolveGitContext } from '
|
|
7
|
+
import { resolveGitContext } from '../../packages/agentsam-repository/src/git-context.js';
|
|
8
8
|
import { getRepositoryId, tryReadProjectConfig } from '../lib/project-config.js';
|
|
9
|
-
import { buildMerkleTree } from '
|
|
10
|
-
import { gitIgnoredPaths } from '
|
|
9
|
+
import { buildMerkleTree } from '../../packages/agentsam-repository/src/merkle/index.js';
|
|
10
|
+
import { gitIgnoredPaths } from '../../packages/agentsam-repository/src/merkle/git-ignore.js';
|
|
11
11
|
import { analyzeExecutionBoundaries } from '../indexing/execution-boundary.js';
|
|
12
12
|
import { showLatestDeployReceipt } from '../lib/deploy-receipt/index.js';
|
|
13
13
|
import { CONFIG_PATH, canonical, readConfig, scopeKey } from '../knowledge/config.js';
|
package/src/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ import { promptOptionalByokKeys } from './lib/prompt-byok.js';
|
|
|
13
13
|
import { runStartLocal } from './commands/start-local.js';
|
|
14
14
|
import { runOllama } from './commands/ollama.js';
|
|
15
15
|
import { runModels } from './commands/models.js';
|
|
16
|
+
import { runEnv } from './commands/env.js';
|
|
16
17
|
import { runTunnel } from './commands/tunnel.js';
|
|
17
18
|
import { runDeploy } from './commands/deploy.js';
|
|
18
19
|
import { runConnections } from './commands/connections.js';
|
|
@@ -42,6 +43,7 @@ import fs from 'node:fs';
|
|
|
42
43
|
import { repositoryRoot } from './knowledge/config.js';
|
|
43
44
|
import { resolveAccountSdkKey } from './lib/account-session.js';
|
|
44
45
|
import { renderDiagnosticError } from './errors/index.js';
|
|
46
|
+
import { renderHelpOverview, runHelp } from './ui/cli/help.js';
|
|
45
47
|
|
|
46
48
|
const VERSION = pkg.version;
|
|
47
49
|
|
|
@@ -60,6 +62,10 @@ function createPrompt() {
|
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
function printHelp() {
|
|
65
|
+
console.log(renderHelpOverview(VERSION));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function printLegacyHelp() {
|
|
63
69
|
console.log(`
|
|
64
70
|
Agent Sam SDK — CLI v${VERSION}
|
|
65
71
|
|
|
@@ -90,9 +96,10 @@ function printHelp() {
|
|
|
90
96
|
agentsam status [--json] Live local Git + DB + API + PTY status
|
|
91
97
|
agentsam db init|status Manage the project-local SQLite database
|
|
92
98
|
agentsam models Verify configured providers and selectable hosted/local models
|
|
93
|
-
agentsam
|
|
94
|
-
agentsam
|
|
95
|
-
agentsam
|
|
99
|
+
agentsam env init <name> Create a secure provider profile + reusable shell loader
|
|
100
|
+
agentsam login Sign in to Inner Animal Media and persist a secure machine-local session
|
|
101
|
+
agentsam logout Sign out locally; provider credentials stay untouched
|
|
102
|
+
agentsam whoami [--json] Authenticated account identity + safe credential status
|
|
96
103
|
agentsam resume [session] Resume a saved Agent Sam session; omit id for picker
|
|
97
104
|
agentsam eval context Offline context-strategy/economics fixtures (--help)
|
|
98
105
|
agentsam cloudflare Native Wrangler reads + Worker CPU profile analysis (--help)
|
|
@@ -102,8 +109,9 @@ function printHelp() {
|
|
|
102
109
|
agentsam tunnel Explicitly expose local PTY when remote access is wanted
|
|
103
110
|
agentsam deploy Graduate to Cloudflare / GCP when ready
|
|
104
111
|
agentsam dockerize Build/run app, knowledge, or CAD containers (--help)
|
|
105
|
-
agentsam identity preview
|
|
112
|
+
agentsam identity preview Preview the reusable local auth portal (not production login)
|
|
106
113
|
agentsam identity init Add reusable identity package surfaces
|
|
114
|
+
agentsam help
|
|
107
115
|
agentsam --version
|
|
108
116
|
agentsam --help
|
|
109
117
|
|
|
@@ -127,8 +135,8 @@ function printHelp() {
|
|
|
127
135
|
--pretty Pretty-print JSON; machine JSON is compact by default
|
|
128
136
|
--remote <name> Preferred Git remote (default origin; falls back to first remote)
|
|
129
137
|
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
Run agentsam from any project to enter the account-aware interactive experience.
|
|
139
|
+
Account, model-provider, terminal, and deploy permissions are requested only when the related capability needs them.
|
|
132
140
|
|
|
133
141
|
Tunnel options:
|
|
134
142
|
--quick Quick tunnel (default) — trycloudflare.com URL
|
|
@@ -311,6 +319,8 @@ const rest = process.argv.slice(3);
|
|
|
311
319
|
|
|
312
320
|
if (command === '--version' || command === '-v') {
|
|
313
321
|
console.log(VERSION);
|
|
322
|
+
} else if (command === 'help') {
|
|
323
|
+
await runHelp(rest, { version: VERSION });
|
|
314
324
|
} else if (command === '--help' || command === '-h') {
|
|
315
325
|
printHelp();
|
|
316
326
|
} else if (!command) {
|
|
@@ -377,6 +387,13 @@ if (command === '--version' || command === '-v') {
|
|
|
377
387
|
reportCliError(e);
|
|
378
388
|
process.exit(1);
|
|
379
389
|
}
|
|
390
|
+
} else if (command === 'env') {
|
|
391
|
+
try {
|
|
392
|
+
await runEnv(rest);
|
|
393
|
+
} catch (e) {
|
|
394
|
+
reportCliError(e);
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
380
397
|
} else if (command === 'eval') {
|
|
381
398
|
try {
|
|
382
399
|
await runEval(rest);
|
|
@@ -12,7 +12,9 @@ function percent(value) {
|
|
|
12
12
|
|
|
13
13
|
export function buildContextEconomicsReport(cwd, options = {}) {
|
|
14
14
|
const preferences = options.preferences || readCliPreferences(cwd) || {};
|
|
15
|
-
const model =
|
|
15
|
+
const model = preferences.modelSnapshot?.model_key === preferences.modelPreference
|
|
16
|
+
? preferences.modelSnapshot
|
|
17
|
+
: getModelRecord(preferences.modelPreference);
|
|
16
18
|
if (!model) {
|
|
17
19
|
return Object.freeze({
|
|
18
20
|
model: preferences.modelPreference || 'auto',
|
|
@@ -24,9 +26,22 @@ export function buildContextEconomicsReport(cwd, options = {}) {
|
|
|
24
26
|
});
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
const windowTokens = Number(model.context_window);
|
|
30
|
+
if (!(Number.isFinite(windowTokens) && windowTokens > 0)) {
|
|
31
|
+
return Object.freeze({
|
|
32
|
+
model: model.provider_model_id,
|
|
33
|
+
model_key: model.model_key,
|
|
34
|
+
resolved: false,
|
|
35
|
+
reason: 'This provider verified the model but did not expose a trustworthy context-window limit. Agent Sam will show ctx unknown rather than guess.',
|
|
36
|
+
reasoning_effort: preferences.reasoningEffort || 'auto',
|
|
37
|
+
service_tier: preferences.serviceTier || 'default',
|
|
38
|
+
active_input_tokens: Number.isFinite(options.activeInputTokens) ? Math.floor(options.activeInputTokens) : null,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
const policy = model.context_policy || {};
|
|
28
43
|
const budget = createContextBudget({
|
|
29
|
-
windowTokens
|
|
44
|
+
windowTokens,
|
|
30
45
|
targetInputTokens: policy.target_input_tokens,
|
|
31
46
|
compactAtTokens: policy.compact_at_tokens,
|
|
32
47
|
interveneAtTokens: policy.intervene_at_tokens,
|
package/src/commands/context.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { tryResolveGitContext } from '
|
|
1
|
+
import { tryResolveGitContext } from '../../packages/agentsam-repository/src/git-context.js';
|
|
2
2
|
import { resolveAgentSamBaseUrl, resolveBridgeKey } from '../lib/bridge-client.js';
|
|
3
3
|
import { loadProjectRules } from '../lib/project-rules.js';
|
|
4
4
|
|
package/src/commands/db.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { initializeLocalSqlite, inspectLocalSqlite } from '../local/sqlite.js';
|
|
3
|
+
import { createLocalSqliteDatabase, initializeLocalSqlite, inspectLocalSqlite } from '../local/sqlite.js';
|
|
4
|
+
import { applyRuntimeMigrations } from '../local/migrations.js';
|
|
4
5
|
import { getLocalDatabasePath, getLocalSchemaPath, readProjectConfig } from '../lib/project-config.js';
|
|
5
6
|
|
|
6
7
|
function findProjectRoot(startDir) {
|
|
@@ -23,7 +24,7 @@ function resolveDb(root, config) {
|
|
|
23
24
|
|
|
24
25
|
export async function runDb(argv = [], opts = {}) {
|
|
25
26
|
const sub = argv[0] || 'status';
|
|
26
|
-
if (!['init', 'status'].includes(sub)) {
|
|
27
|
+
if (!['init', 'migrate', 'status'].includes(sub)) {
|
|
27
28
|
throw new Error(`unknown db command: ${sub}`);
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -36,10 +37,26 @@ export async function runDb(argv = [], opts = {}) {
|
|
|
36
37
|
console.log(`\n Agent Sam local DB\n`);
|
|
37
38
|
console.log(` ✓ SQLite ${result.dbPath}`);
|
|
38
39
|
console.log(` ✓ Tables ${result.tables.length}`);
|
|
39
|
-
console.log(` ✓ Schema ${paths.schemaPath}
|
|
40
|
+
console.log(` ✓ Schema ${paths.schemaPath}`);
|
|
41
|
+
console.log(' ✓ Migrations current\n');
|
|
40
42
|
return result;
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
if (sub === 'migrate') {
|
|
46
|
+
if (!fs.existsSync(paths.dbPath)) throw new Error('Local DB is not initialized — run `agentsam db init` first.');
|
|
47
|
+
const db = await createLocalSqliteDatabase(paths.dbPath);
|
|
48
|
+
try {
|
|
49
|
+
const result = await applyRuntimeMigrations(db);
|
|
50
|
+
console.log(`\n Agent Sam local DB migrations\n`);
|
|
51
|
+
console.log(` applied ${result.applied}`);
|
|
52
|
+
console.log(` known ${result.total}`);
|
|
53
|
+
console.log(` path ${paths.dbPath}\n`);
|
|
54
|
+
return result;
|
|
55
|
+
} finally {
|
|
56
|
+
db.close();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
const result = await inspectLocalSqlite(paths.dbPath);
|
|
44
61
|
console.log(`\n Agent Sam local DB\n`);
|
|
45
62
|
if (!result.exists) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { describeProviderCredential, ensureProviderEnvProfile, providerCredentialSpec } from '../lib/provider-credentials.js';
|
|
3
|
+
|
|
4
|
+
const PROVIDERS = Object.freeze(['openai', 'anthropic', 'gemini', 'grok', 'cloudflare']);
|
|
5
|
+
|
|
6
|
+
function writeLine(write, value = '') { write(`${value}\n`); }
|
|
7
|
+
|
|
8
|
+
function detectCloudflareAccounts(options = {}) {
|
|
9
|
+
const env = options.env || process.env;
|
|
10
|
+
const explicit = String(options.accountId || env.ACCOUNT_ID || env.CLOUDFLARE_ACCOUNT_ID || '').trim();
|
|
11
|
+
if (explicit) return { accounts: [{ id: explicit, name: null }], source: 'environment' };
|
|
12
|
+
const spawn = options.spawnSyncImpl || spawnSync;
|
|
13
|
+
const result = spawn('npx', ['--no-install', 'wrangler', 'whoami', '--json'], { cwd: options.cwd || process.cwd(), env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
14
|
+
if (result?.status !== 0) return { accounts: [], source: 'wrangler', error: String(result?.stderr || '').trim() || `wrangler exited ${result?.status ?? 'unknown'}` };
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(String(result.stdout || '{}'));
|
|
17
|
+
const accounts = (Array.isArray(parsed?.accounts) ? parsed.accounts : []).map((row) => ({
|
|
18
|
+
id: String(row?.id || row?.account_id || '').trim(),
|
|
19
|
+
name: String(row?.name || row?.account_name || '').trim() || null,
|
|
20
|
+
})).filter((row) => row.id);
|
|
21
|
+
return { accounts, source: 'wrangler', error: null };
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return { accounts: [], source: 'wrangler', error: `invalid wrangler JSON: ${error?.message || error}` };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function cloudflareGuidance(write) {
|
|
28
|
+
writeLine(write, '');
|
|
29
|
+
writeLine(write, ' Cloudflare token guidance');
|
|
30
|
+
writeLine(write, ' models only Workers AI Read');
|
|
31
|
+
writeLine(write, ' run Workers AI Workers AI Read + Edit');
|
|
32
|
+
writeLine(write, ' deploy Workers Workers Editor for existing Workers; Admin only when create/delete is required');
|
|
33
|
+
writeLine(write, ' routes/domains add Workers Routes Write only when AgentSam must change them');
|
|
34
|
+
writeLine(write, ' D1 / R2 / KV add direct product permissions only when AgentSam must read/write those resources directly');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function runEnv(argv = [], options = {}) {
|
|
38
|
+
const write = options.write || ((text) => process.stdout.write(text));
|
|
39
|
+
const [command = 'status', providerArg] = argv;
|
|
40
|
+
let accountId = '';
|
|
41
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
42
|
+
if (argv[i] === '--account-id') accountId = String(argv[++i] || '').trim();
|
|
43
|
+
else throw new Error(`unexpected env argument: ${argv[i]}`);
|
|
44
|
+
}
|
|
45
|
+
if (command === '--help' || command === '-h' || command === 'help') {
|
|
46
|
+
writeLine(write, 'agentsam env init <openai|anthropic|gemini|grok|cloudflare> [--account-id <id>]');
|
|
47
|
+
writeLine(write, 'agentsam env status [provider]');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (command === 'init') {
|
|
51
|
+
const provider = String(providerArg || '').trim().toLowerCase();
|
|
52
|
+
if (!PROVIDERS.includes(provider)) throw new Error(`env init requires one of: ${PROVIDERS.join(', ')}`);
|
|
53
|
+
let cloudflareAccounts = null;
|
|
54
|
+
if (provider === 'cloudflare') {
|
|
55
|
+
if (accountId && !/^[a-f0-9]{32}$/i.test(accountId)) throw new Error('Cloudflare --account-id must be a 32-character hexadecimal account ID');
|
|
56
|
+
cloudflareAccounts = detectCloudflareAccounts({ ...options, accountId });
|
|
57
|
+
if (!accountId && cloudflareAccounts.accounts.length === 1) accountId = cloudflareAccounts.accounts[0].id;
|
|
58
|
+
}
|
|
59
|
+
const result = ensureProviderEnvProfile(provider, { ...options, accountId });
|
|
60
|
+
writeLine(write, '');
|
|
61
|
+
writeLine(write, ` AgentSam · ${provider} environment`);
|
|
62
|
+
writeLine(write, ` profile ${result.file}${result.created ? ' · created' : ' · existing'}`);
|
|
63
|
+
writeLine(write, ` loader ${result.loader}`);
|
|
64
|
+
writeLine(write, '');
|
|
65
|
+
writeLine(write, ' Add the credential to the profile, then load it into this shell:');
|
|
66
|
+
writeLine(write, ` ${result.source_command}`);
|
|
67
|
+
if (provider === 'cloudflare') {
|
|
68
|
+
if (accountId) writeLine(write, ` account detected/configured (${cloudflareAccounts?.source || 'explicit'})`);
|
|
69
|
+
else if ((cloudflareAccounts?.accounts || []).length > 1) writeLine(write, ` account ${cloudflareAccounts.accounts.length} Wrangler accounts found · rerun with --account-id <id>`);
|
|
70
|
+
else writeLine(write, ' account not detected · set ACCOUNT_ID in the profile or rerun with --account-id <id>');
|
|
71
|
+
cloudflareGuidance(write);
|
|
72
|
+
}
|
|
73
|
+
writeLine(write, '');
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
if (command === 'status') {
|
|
77
|
+
const providers = providerArg ? [String(providerArg).trim().toLowerCase()] : PROVIDERS;
|
|
78
|
+
for (const provider of providers) if (!providerCredentialSpec(provider)) throw new Error(`unsupported_provider:${provider}`);
|
|
79
|
+
writeLine(write, '');
|
|
80
|
+
writeLine(write, ' AgentSam · provider environments');
|
|
81
|
+
for (const provider of providers) {
|
|
82
|
+
const row = describeProviderCredential(provider, options);
|
|
83
|
+
const extra = row.account_id ? ` · account ${row.account_id}` : '';
|
|
84
|
+
writeLine(write, ` ${provider.padEnd(12)} ${row.configured ? 'configured' : row.error ? `blocked (${row.error})` : 'not configured'}${extra}`);
|
|
85
|
+
}
|
|
86
|
+
writeLine(write, '');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
throw new Error(`unknown env command: ${command}`);
|
|
90
|
+
}
|
|
@@ -7,13 +7,13 @@ import { promisify } from 'node:util';
|
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { randomUUID } from 'node:crypto';
|
|
9
9
|
import pkg from '../../package.json' with { type: 'json' };
|
|
10
|
-
import { CONFIG_PATH, repositoryRoot, initRepository, readConfig, scopeKey, cacheNamespace } from '../knowledge/config.js';
|
|
10
|
+
import { CONFIG_PATH, repositoryRoot, initRepository, readConfig, defaultConfig, scopeKey, cacheNamespace } from '../knowledge/config.js';
|
|
11
11
|
import { openSqliteStore } from '../knowledge/stores/sqlite.js';
|
|
12
12
|
import { openPostgresStore } from '../knowledge/stores/postgres.js';
|
|
13
13
|
import { planIndex, runIndex, retrieve } from '../knowledge/engine.js';
|
|
14
14
|
import { createGeminiEmbedder } from '../knowledge/providers/gemini.js';
|
|
15
15
|
import { compareObservations } from '../knowledge/evolution.js';
|
|
16
|
-
import { ensureProjectManifest, getProjectName, getRepositoryId } from '../lib/project-config.js';
|
|
16
|
+
import { ensureProjectManifest, getProjectName, getRepositoryId, portableRepositoryIdFromGit, tryReadProjectConfig } from '../lib/project-config.js';
|
|
17
17
|
import { ensureProjectRules } from '../lib/project-rules.js';
|
|
18
18
|
|
|
19
19
|
const execute = promisify(execFile);
|
|
@@ -25,6 +25,14 @@ async function openStore(root, config, readOnly = false) {
|
|
|
25
25
|
return config.storage.driver === 'sqlite' ? openSqliteStore(localPath(root), { readOnly }) : openPostgresStore(process.env[config.storage.connection_env]);
|
|
26
26
|
}
|
|
27
27
|
function provider() { return createGeminiEmbedder({ apiKey: process.env.GEMINI_API_KEY }); }
|
|
28
|
+
function resolveKnowledgeConfig(root) {
|
|
29
|
+
const filename = path.join(root, CONFIG_PATH);
|
|
30
|
+
if (fs.existsSync(filename)) return readConfig(root);
|
|
31
|
+
const project = tryReadProjectConfig(root);
|
|
32
|
+
const repositoryId = getRepositoryId(project) || portableRepositoryIdFromGit(root);
|
|
33
|
+
if (!repositoryId) throw new Error(`${CONFIG_PATH} is absent and repository identity could not be derived; run agentsam init . --yes to configure this repository.`);
|
|
34
|
+
return defaultConfig({ repositoryId });
|
|
35
|
+
}
|
|
28
36
|
|
|
29
37
|
export async function runRepositoryInit(argv) {
|
|
30
38
|
const { values: opts, positionals } = flags(argv, { existing: { type: 'boolean' }, yes: { type: 'boolean', short: 'y' }, include: { type: 'string' }, exclude: { type: 'string' }, scope: { type: 'string' }, target: { type: 'string' }, dimensions: { type: 'string' } });
|
|
@@ -73,7 +81,7 @@ export async function runKnowledge(argv) {
|
|
|
73
81
|
const command = positionals[0] || 'plan';
|
|
74
82
|
if (opts.help) { console.log('agentsam index plan|run|status|history|show|setup-store [--cwd PATH] [--embed] [--max-inputs 100] [--max-characters 200000] [--generation ID] [--json]\nplan is read-only; run defaults to AST/text only; --embed sends selected chunks to the configured provider.'); return; }
|
|
75
83
|
if (positionals.length > 1 || !['plan', 'run', 'status', 'history', 'show', 'setup-store'].includes(command)) throw new Error('Unknown index command; use agentsam index --help.');
|
|
76
|
-
const root = repositoryRoot(opts.cwd), config =
|
|
84
|
+
const root = repositoryRoot(opts.cwd), config = resolveKnowledgeConfig(root);
|
|
77
85
|
const store = await openStore(root, config, !['run', 'setup-store'].includes(command));
|
|
78
86
|
try {
|
|
79
87
|
if (command === 'setup-store') {
|
|
@@ -97,7 +105,7 @@ export async function runKnowledge(argv) {
|
|
|
97
105
|
export async function runSearch(argv) {
|
|
98
106
|
const { values: opts, positionals } = flags(argv, { semantic: { type: 'boolean' }, 'top-k': { type: 'string' }, 'token-budget': { type: 'string' }, generation: { type: 'string' } });
|
|
99
107
|
if (opts.help) { console.log('agentsam search "query" [--cwd PATH] [--semantic] [--top-k 8] [--token-budget 6000] [--generation ID]'); return; }
|
|
100
|
-
const root = repositoryRoot(opts.cwd), config =
|
|
108
|
+
const root = repositoryRoot(opts.cwd), config = resolveKnowledgeConfig(root), store = await openStore(root, config, true);
|
|
101
109
|
try { show(await retrieve({ store, config, text: positionals.join(' '), semantic: opts.semantic, embedder: opts.semantic ? provider() : null, topK: Number(opts['top-k'] || 8), tokenBudget: Number(opts['token-budget'] || 6000), generationId: opts.generation })); }
|
|
102
110
|
finally { await store?.close(); }
|
|
103
111
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { readSnapshot } from '
|
|
2
|
+
import { readSnapshot } from '../../packages/agentsam-repository/src/merkle/snapshot.js';
|
|
3
|
+
import { readAccountSession } from '../lib/account-session.js';
|
|
4
|
+
import { getRepositoryId, portableRepositoryIdFromGit, tryReadProjectConfig } from '../lib/project-config.js';
|
|
3
5
|
import {
|
|
4
6
|
buildMerklePersistencePlan,
|
|
5
7
|
persistMerkleSnapshotCloudflare,
|
|
6
8
|
resolveWranglerMerklePersistence,
|
|
7
|
-
} from '
|
|
9
|
+
} from '../../packages/agentsam-repository/src/merkle/cloudflare-persistence.js';
|
|
8
10
|
|
|
9
11
|
function value(args, index, flag) {
|
|
10
12
|
const next = args[index + 1];
|
|
@@ -17,8 +19,6 @@ export function printMerklePersistHelp() {
|
|
|
17
19
|
agentsam merkle persist <snapshot.json> — publish a saved Merkle snapshot through host bindings
|
|
18
20
|
|
|
19
21
|
--wrangler-config <file> Worker config containing WEBSITE_ASSETS and optionally DB
|
|
20
|
-
--owner-user-id <id> Snapshot owner authority (or AGENTSAM_OWNER_USER_ID)
|
|
21
|
-
--repo-id <id> Canonical repo id; inferred for GitHub/GitLab/Bitbucket when possible
|
|
22
22
|
--capture-kind <kind> deploy|manual|agent|index (default manual)
|
|
23
23
|
--connection-id <id> Execution provenance for non-deploy captures
|
|
24
24
|
--runtime-lease-id <id> Alternative execution provenance for non-deploy captures
|
|
@@ -35,9 +35,11 @@ export function printMerklePersistHelp() {
|
|
|
35
35
|
--dry-run Resolve bindings and emit the exact storage/index plan without writes
|
|
36
36
|
--json Machine-readable output
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
CLI ownership comes from the authenticated AgentSam session, while repository identity is
|
|
39
|
+
derived from Git/provider identity (with the committed project manifest as local fallback).
|
|
40
|
+
Programmatic hosts pass account_id + repository_id directly to the persistence plan. Physical
|
|
41
|
+
bucket/database names come from Wrangler bindings, so customer installs keep WEBSITE_ASSETS/DB
|
|
42
|
+
while selecting their own storage resources.
|
|
41
43
|
`);
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -55,7 +57,7 @@ function parse(args) {
|
|
|
55
57
|
if (arg === '--dry-run') { opts.dryRun = true; continue; }
|
|
56
58
|
if (arg === '--r2-only') { opts.r2Only = true; continue; }
|
|
57
59
|
const map = {
|
|
58
|
-
'--wrangler-config': 'wranglerConfig',
|
|
60
|
+
'--wrangler-config': 'wranglerConfig',
|
|
59
61
|
'--capture-kind': 'captureKind', '--connection-id': 'connectionId', '--runtime-lease-id': 'runtimeLeaseId',
|
|
60
62
|
'--deployment-id': 'deploymentId', '--worker-version': 'workerVersionId', '--reference-label': 'referenceLabel',
|
|
61
63
|
'--source': 'source', '--prefix': 'storagePrefix', '--r2-binding': 'r2Binding', '--d1-binding': 'd1Binding',
|
|
@@ -65,19 +67,36 @@ function parse(args) {
|
|
|
65
67
|
throw new Error(`Unknown merkle persist option: ${arg}`);
|
|
66
68
|
}
|
|
67
69
|
if (!opts.snapshotPath) throw new Error('snapshot_file_required');
|
|
68
|
-
opts.ownerUserId ||= process.env.AGENTSAM_OWNER_USER_ID || '';
|
|
69
70
|
opts.wranglerConfig ||= process.env.AGENTSAM_WRANGLER_CONFIG || '';
|
|
70
71
|
opts.connectionId ||= process.env.AGENTSAM_CONNECTION_ID || '';
|
|
71
72
|
opts.runtimeLeaseId ||= process.env.AGENTSAM_RUNTIME_LEASE_ID || '';
|
|
72
73
|
return opts;
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
export function resolveMerklePersistenceIdentity(root, options = {}) {
|
|
77
|
+
const session = options.session ?? readAccountSession(options.sessionOptions || {});
|
|
78
|
+
const accountId = String(session?.account_id || '').trim();
|
|
79
|
+
if (!accountId) throw new Error('agentsam_login_required_for_merkle_persistence');
|
|
80
|
+
|
|
81
|
+
const projectConfig = options.projectConfig ?? tryReadProjectConfig(root);
|
|
82
|
+
const gitRepositoryId = portableRepositoryIdFromGit(root);
|
|
83
|
+
const repositoryId = gitRepositoryId || getRepositoryId(projectConfig);
|
|
84
|
+
if (!repositoryId) throw new Error('repository_identity_unresolved');
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
accountId,
|
|
88
|
+
repositoryId,
|
|
89
|
+
repositoryIdentitySource: gitRepositoryId ? 'git' : 'project_manifest',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
75
93
|
export async function runMerklePersist(args = []) {
|
|
76
94
|
const opts = parse(args);
|
|
77
95
|
if (opts.help) { printMerklePersistHelp(); return null; }
|
|
78
96
|
const snapshotPath = path.resolve(opts.snapshotPath);
|
|
79
97
|
const snapshot = await readSnapshot(snapshotPath);
|
|
80
98
|
const root = path.resolve(opts.root || snapshot.rootPath || process.cwd());
|
|
99
|
+
const identity = resolveMerklePersistenceIdentity(root);
|
|
81
100
|
const wrangler = resolveWranglerMerklePersistence({
|
|
82
101
|
configPath: opts.wranglerConfig,
|
|
83
102
|
environment: opts.environment || null,
|
|
@@ -88,8 +107,8 @@ export async function runMerklePersist(args = []) {
|
|
|
88
107
|
const plan = buildMerklePersistencePlan({
|
|
89
108
|
snapshot,
|
|
90
109
|
root,
|
|
91
|
-
|
|
92
|
-
|
|
110
|
+
accountId: identity.accountId,
|
|
111
|
+
repositoryId: identity.repositoryId,
|
|
93
112
|
source: opts.source,
|
|
94
113
|
captureKind: opts.captureKind,
|
|
95
114
|
connectionId: opts.connectionId,
|