@inneranimalmedia/agentsam-sdk 2.6.0 → 2.6.2
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/AUTH_IDENTITY_CONTRACT.md +25 -27
- package/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
- package/docs/RELEASES.md +7 -7
- package/docs/SDK_WORKER.md +4 -3
- 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 +2 -2
- package/packages/identity/src/contracts/auth-config.js +23 -40
- package/packages/identity/tests/auth-config.test.mjs +14 -23
- 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 +36 -29
- package/src/commands/account-auth.js +1 -1
- 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/deploy.js +2 -2
- 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/providers.js +308 -0
- package/src/commands/shell.js +240 -48
- package/src/commands/tunnel.js +8 -8
- package/src/commands/whoami.js +42 -21
- package/src/errors/contract.js +236 -0
- package/src/errors/diagnostic.js +1 -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/account-session.js +94 -25
- package/src/lib/auth.js +314 -51
- package/src/lib/cli-preferences.js +31 -4
- package/src/lib/core-client.js +75 -30
- package/src/lib/deploy-receipt/index.js +2 -2
- package/src/lib/detect-context.js +14 -16
- 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 +230 -14
- package/src/lib/slash-commands.js +5 -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 +327 -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 +194 -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/account-session.test.mjs +48 -11
- package/test/apps-scaffold-contract.test.mjs +43 -0
- 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/sdk-worker-contract.test.mjs +5 -1
- package/test/shell.test.mjs +50 -8
- package/test/smoke.mjs +2 -1
- package/test/terminal/local-pty.mock.test.mjs +151 -0
- package/test/whoami-resume.test.mjs +6 -5
- package/src/lib/prompt-byok.js +0 -57
- package/src/lib/save-sdk-token.js +0 -19
- /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
package/src/cli.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import pkg from '../package.json' with { type: 'json' };
|
|
4
|
-
import readline from 'readline';
|
|
5
4
|
import { cancel, intro, isCancel, outro, select, text } from '@clack/prompts';
|
|
6
5
|
import path from 'node:path';
|
|
7
6
|
import { buildLocalScaffoldMeta } from './lib/local-scaffold.js';
|
|
@@ -9,10 +8,11 @@ import { writeScaffoldFiles } from './lib/write-files.js';
|
|
|
9
8
|
import { initializeGitRepository } from './lib/init-git.js';
|
|
10
9
|
import { initializeLocalSqlite } from './local/sqlite.js';
|
|
11
10
|
import { printContextSummary } from './lib/detect-context.js';
|
|
12
|
-
import { promptOptionalByokKeys } from './lib/prompt-byok.js';
|
|
13
11
|
import { runStartLocal } from './commands/start-local.js';
|
|
14
12
|
import { runOllama } from './commands/ollama.js';
|
|
15
13
|
import { runModels } from './commands/models.js';
|
|
14
|
+
import { runProviders } from './commands/providers.js';
|
|
15
|
+
import { runEnv } from './commands/env.js';
|
|
16
16
|
import { runTunnel } from './commands/tunnel.js';
|
|
17
17
|
import { runDeploy } from './commands/deploy.js';
|
|
18
18
|
import { runConnections } from './commands/connections.js';
|
|
@@ -40,8 +40,9 @@ import { applyPresetSelection, runAdd, runCapabilities, runDev, runInspect } fro
|
|
|
40
40
|
import { listPresets, resolvePreset } from './presets/index.js';
|
|
41
41
|
import fs from 'node:fs';
|
|
42
42
|
import { repositoryRoot } from './knowledge/config.js';
|
|
43
|
-
import {
|
|
43
|
+
import { resolveAccountAuth } from './lib/account-session.js';
|
|
44
44
|
import { renderDiagnosticError } from './errors/index.js';
|
|
45
|
+
import { renderHelpOverview, runHelp } from './ui/cli/help.js';
|
|
45
46
|
|
|
46
47
|
const VERSION = pkg.version;
|
|
47
48
|
|
|
@@ -51,15 +52,12 @@ function reportCliError(error) {
|
|
|
51
52
|
console.error(`\n${rendered}\n`);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
function createPrompt() {
|
|
55
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
56
|
-
return {
|
|
57
|
-
ask: (q) => new Promise((resolve) => rl.question(q, resolve)),
|
|
58
|
-
close: () => rl.close(),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
55
|
|
|
62
56
|
function printHelp() {
|
|
57
|
+
console.log(renderHelpOverview(VERSION));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function printLegacyHelp() {
|
|
63
61
|
console.log(`
|
|
64
62
|
Agent Sam SDK — CLI v${VERSION}
|
|
65
63
|
|
|
@@ -90,9 +88,11 @@ function printHelp() {
|
|
|
90
88
|
agentsam status [--json] Live local Git + DB + API + PTY status
|
|
91
89
|
agentsam db init|status Manage the project-local SQLite database
|
|
92
90
|
agentsam models Verify configured providers and selectable hosted/local models
|
|
93
|
-
agentsam
|
|
94
|
-
agentsam
|
|
95
|
-
agentsam
|
|
91
|
+
agentsam providers Configure, verify, and remove machine provider credentials
|
|
92
|
+
agentsam env init <name> Create a secure provider profile + reusable shell loader
|
|
93
|
+
agentsam login Sign in to Inner Animal Media and persist a secure machine-local session
|
|
94
|
+
agentsam logout Sign out locally; provider credentials stay untouched
|
|
95
|
+
agentsam whoami [--json] Authenticated account identity + safe credential status
|
|
96
96
|
agentsam resume [session] Resume a saved Agent Sam session; omit id for picker
|
|
97
97
|
agentsam eval context Offline context-strategy/economics fixtures (--help)
|
|
98
98
|
agentsam cloudflare Native Wrangler reads + Worker CPU profile analysis (--help)
|
|
@@ -102,8 +102,9 @@ function printHelp() {
|
|
|
102
102
|
agentsam tunnel Explicitly expose local PTY when remote access is wanted
|
|
103
103
|
agentsam deploy Graduate to Cloudflare / GCP when ready
|
|
104
104
|
agentsam dockerize Build/run app, knowledge, or CAD containers (--help)
|
|
105
|
-
agentsam identity preview
|
|
105
|
+
agentsam identity preview Preview the reusable local auth portal (not production login)
|
|
106
106
|
agentsam identity init Add reusable identity package surfaces
|
|
107
|
+
agentsam help
|
|
107
108
|
agentsam --version
|
|
108
109
|
agentsam --help
|
|
109
110
|
|
|
@@ -127,14 +128,14 @@ function printHelp() {
|
|
|
127
128
|
--pretty Pretty-print JSON; machine JSON is compact by default
|
|
128
129
|
--remote <name> Preferred Git remote (default origin; falls back to first remote)
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
Run agentsam from any project to enter the account-aware interactive experience.
|
|
132
|
+
Account, model-provider, terminal, and deploy permissions are requested only when the related capability needs them.
|
|
132
133
|
|
|
133
134
|
Tunnel options:
|
|
134
135
|
--quick Quick tunnel (default) — trycloudflare.com URL
|
|
135
136
|
--named Named CF tunnel (needs --tunnel-name --hostname --zone-id)
|
|
136
137
|
--port <n> Local PTY port (default 3099)
|
|
137
|
-
--token <
|
|
138
|
+
--token <aak_…> Use AGENTSAM_API_KEY-compatible account credential
|
|
138
139
|
|
|
139
140
|
Init options:
|
|
140
141
|
--name <name> Project directory name
|
|
@@ -227,11 +228,6 @@ async function runLocalInit(config) {
|
|
|
227
228
|
console.log(` ${step}`);
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
const sdkKey = resolveAccountSdkKey({ env: process.env }).value;
|
|
231
|
-
if (prompt && sdkKey) {
|
|
232
|
-
console.log('\n Optional — BYOK keys for IAM dashboard Agent Sam (skip with Enter):\n');
|
|
233
|
-
await promptOptionalByokKeys(sdkKey, prompt);
|
|
234
|
-
}
|
|
235
231
|
|
|
236
232
|
console.log(`
|
|
237
233
|
Local means local: no Worker, tunnel, IAM login, or cloud database is required.
|
|
@@ -283,17 +279,12 @@ async function initInteractive(partial = {}) {
|
|
|
283
279
|
if (runTarget !== 'local') {
|
|
284
280
|
const { detectContext, missingForInit } = await import('./lib/detect-context.js');
|
|
285
281
|
const ctx = await detectContext();
|
|
286
|
-
if (missingForInit(ctx,
|
|
282
|
+
if (missingForInit(ctx, resolveAccountAuth({ env: process.env }).value, { runTarget }).length) {
|
|
287
283
|
printContextSummary(ctx);
|
|
288
284
|
}
|
|
289
285
|
}
|
|
290
286
|
|
|
291
|
-
|
|
292
|
-
try {
|
|
293
|
-
await runLocalInit({ projectName, lane: laneKey, runTarget, prompt });
|
|
294
|
-
} finally {
|
|
295
|
-
prompt?.close();
|
|
296
|
-
}
|
|
287
|
+
await runLocalInit({ projectName, lane: laneKey, runTarget, prompt: null });
|
|
297
288
|
outro(`Created ${projectName}`);
|
|
298
289
|
}
|
|
299
290
|
|
|
@@ -311,6 +302,8 @@ const rest = process.argv.slice(3);
|
|
|
311
302
|
|
|
312
303
|
if (command === '--version' || command === '-v') {
|
|
313
304
|
console.log(VERSION);
|
|
305
|
+
} else if (command === 'help') {
|
|
306
|
+
await runHelp(rest, { version: VERSION });
|
|
314
307
|
} else if (command === '--help' || command === '-h') {
|
|
315
308
|
printHelp();
|
|
316
309
|
} else if (!command) {
|
|
@@ -377,6 +370,20 @@ if (command === '--version' || command === '-v') {
|
|
|
377
370
|
reportCliError(e);
|
|
378
371
|
process.exit(1);
|
|
379
372
|
}
|
|
373
|
+
} else if (command === 'providers') {
|
|
374
|
+
try {
|
|
375
|
+
await runProviders(rest);
|
|
376
|
+
} catch (e) {
|
|
377
|
+
reportCliError(e);
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
} else if (command === 'env') {
|
|
381
|
+
try {
|
|
382
|
+
await runEnv(rest);
|
|
383
|
+
} catch (e) {
|
|
384
|
+
reportCliError(e);
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
380
387
|
} else if (command === 'eval') {
|
|
381
388
|
try {
|
|
382
389
|
await runEval(rest);
|
|
@@ -11,7 +11,7 @@ export async function runLogin(argv = [], options = {}) {
|
|
|
11
11
|
const write = options.write || ((text) => process.stdout.write(text));
|
|
12
12
|
const authenticate = options.authenticateImpl || authenticateViaBrowser;
|
|
13
13
|
const session = await authenticate();
|
|
14
|
-
if (!String(session?.access_token || '').trim()
|
|
14
|
+
if (!String(session?.access_token || '').trim()) throw new Error('Agent Sam login did not return a browser session credential');
|
|
15
15
|
// authenticateViaBrowser persists by default. Keep injected transports/test flows equivalent.
|
|
16
16
|
if (!readAccountSession({ home: options.home })) saveAccountSession(session, { home: options.home });
|
|
17
17
|
const status = await collectWhoami({ home: options.home, env: options.env || process.env, contextLoader: options.contextLoader });
|
|
@@ -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) {
|
package/src/commands/deploy.js
CHANGED
|
@@ -6,7 +6,7 @@ import path from 'node:path';
|
|
|
6
6
|
import readline from 'node:readline';
|
|
7
7
|
import { authenticateViaBrowser } from '../lib/auth.js';
|
|
8
8
|
import { getJson, streamScaffold } from '../lib/core-client.js';
|
|
9
|
-
import {
|
|
9
|
+
import { resolveAccountAuth } from '../lib/account-session.js';
|
|
10
10
|
import { getDefaultProfile, getDeployTarget, getLocalSchemaPath, getProjectName, getProjectPreset, readProjectConfig, setDeployTarget, writeProjectConfig } from '../lib/project-config.js';
|
|
11
11
|
import { isLocalStudioCheckout, runLocalStudioDeploy } from '../lib/deploy/local-studio.js';
|
|
12
12
|
|
|
@@ -68,7 +68,7 @@ function ask(question) {
|
|
|
68
68
|
async function runCloudflareDeploy(cwd, config, accountId) {
|
|
69
69
|
console.log('\n Cloudflare deploy — browser sign-in + resource provisioning…\n');
|
|
70
70
|
|
|
71
|
-
let token =
|
|
71
|
+
let token = resolveAccountAuth({ env: process.env }).value;
|
|
72
72
|
if (!token) {
|
|
73
73
|
const session = await authenticateViaBrowser();
|
|
74
74
|
token = session.access_token;
|
|
@@ -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,
|
package/src/commands/merkle.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { buildMerkleTree, saveSnapshot, readSnapshot, diffTrees, normalizePolicy } from '
|
|
3
|
+
import { buildMerkleTree, saveSnapshot, readSnapshot, diffTrees, normalizePolicy } from '../../packages/agentsam-repository/src/merkle/index.js';
|
|
4
4
|
import { renderSummary, safeText } from '../ui/merkle/render.js';
|
|
5
5
|
import { runMerkleExplorer } from '../ui/merkle/explorer.js';
|
|
6
6
|
import { runMerklePersist } from './merkle-persist.js';
|