@tangle-network/agent-app 0.45.26 → 0.45.27

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.
@@ -83,15 +83,15 @@ import {
83
83
  setPagePropsCommand,
84
84
  ungroupElementCommand
85
85
  } from "../chunk-X5DQ5QOP.js";
86
- import {
87
- DesignCanvasChromeLazy,
88
- DesignCanvasLazy
89
- } from "../chunk-AGCRCOQH.js";
90
86
  import {
91
87
  assertSceneMediaSrc,
92
88
  bleedAwareExportBounds,
93
89
  scaleForPreset
94
90
  } from "../chunk-DRB77FYL.js";
91
+ import {
92
+ DesignCanvasChromeLazy,
93
+ DesignCanvasLazy
94
+ } from "../chunk-AWPK7ZDS.js";
95
95
  import "../chunk-F7VH4G5C.js";
96
96
 
97
97
  // src/design-canvas-react/components/CanvasInsertPanel.tsx
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DesignCanvasChromeLazy,
3
3
  DesignCanvasLazy
4
- } from "../chunk-AGCRCOQH.js";
4
+ } from "../chunk-AWPK7ZDS.js";
5
5
  export {
6
6
  DesignCanvasChromeLazy,
7
7
  DesignCanvasLazy
@@ -1237,6 +1237,57 @@ interface EnsureWorkspaceSandboxOptions {
1237
1237
  forceNew?: boolean;
1238
1238
  onProgress?: (event: ProvisionEvent) => void;
1239
1239
  billingOwnerId?: string;
1240
+ spend?: SandboxSpendHooks;
1241
+ }
1242
+ /** What `/sandbox` reports once a box is provisioned, reused, or resumed. */
1243
+ interface SandboxProvisionedObservation {
1244
+ readonly workspaceId: string;
1245
+ readonly userId?: string;
1246
+ /** The platform's sandbox id — the join key to every settlement row. */
1247
+ readonly sandboxId: string;
1248
+ /** The deterministic box key this workspace resolves to. */
1249
+ readonly boxKey?: string | undefined;
1250
+ /** The idle timeout this box was asked to run with, seconds. */
1251
+ readonly idleTimeoutSeconds: number;
1252
+ /** The max lifetime it was asked to run with, seconds, when one was asked for. */
1253
+ readonly maxLifetimeSeconds?: number | undefined;
1254
+ readonly at: number;
1255
+ }
1256
+ /**
1257
+ * Optional spend-verification seam on `ensureWorkspaceSandbox`.
1258
+ *
1259
+ * The two halves have deliberately opposite failure contracts.
1260
+ * `beforeProvision` is a GATE: it runs before anything is created and its throw
1261
+ * propagates, because refusing to provision is the whole point of a budget cap.
1262
+ * `onProvisioned` is an OBSERVER: its failures are swallowed, because
1263
+ * bookkeeping must never take down the provisioning it is bookkeeping.
1264
+ */
1265
+ interface SandboxSpendHooks {
1266
+ /** Runs before any create, resume, or reuse. Throw to refuse provisioning. */
1267
+ beforeProvision?(input: {
1268
+ workspaceId: string;
1269
+ userId?: string;
1270
+ }): Promise<void> | void;
1271
+ /** Runs after a box is available. Throwing here cannot fail the provision. */
1272
+ onProvisioned?(observation: SandboxProvisionedObservation): Promise<void> | void;
1273
+ /**
1274
+ * The box is doing work. Fired by the turn primitives at the start of a turn
1275
+ * and again when it settles.
1276
+ *
1277
+ * Deliberately SYNCHRONOUS and unawaited — this sits on the turn path, and a
1278
+ * store write must not add latency to it or hold a stream open. An
1279
+ * implementation that persists should enqueue and return; errors are
1280
+ * swallowed here as they are for `onProvisioned`.
1281
+ *
1282
+ * Wiring it is what keeps the expectation ceiling honest for long turns:
1283
+ * without it, `lastActivityAt` only advances when a box is provisioned, so a
1284
+ * three-hour turn leaves the ceiling three hours too tight and manufactures a
1285
+ * discrepancy out of the product's own silence.
1286
+ */
1287
+ onActivity?(input: {
1288
+ sandboxId: string;
1289
+ at: number;
1290
+ }): void;
1240
1291
  }
1241
1292
  /** Define the specification for a sandbox tool including its name, content, and optional executability */
1242
1293
  interface SandboxToolSpec {
@@ -1473,6 +1524,7 @@ interface StreamSandboxPromptOptions {
1473
1524
  };
1474
1525
  detach?: boolean;
1475
1526
  onProfileResolved?: (fingerprint: ProfileFingerprint) => void;
1527
+ spend?: SandboxSpendHooks;
1476
1528
  }
1477
1529
  /**
1478
1530
  * The small event shape consumed by agent-gateway's streaming adapters.
@@ -1605,4 +1657,4 @@ declare function isTerminalPromptEvent(event: unknown): boolean;
1605
1657
  /** Resolve the interactive question text from a structured event or return null if none found */
1606
1658
  declare function detectInteractiveQuestion(event: unknown): string | null;
1607
1659
 
1608
- export { type AppToolDescriptor, type BuildAppToolMcpServersOptions, type BuildSandboxToolFileMountsOptions, type D1PrewarmClaimStoreOptions, DEFAULT_PREWARM_CLAIM_TABLE, DEFAULT_PROVISION_TIMEOUT_MS, DEFAULT_SANDBOX_RESOURCES, DEFAULT_SIDECAR_PROCESS_PATTERN, type DriveSandboxTurnOptions, EGRESS_PROXY_RECOVERY_PHASE, EGRESS_PROXY_RECOVERY_REQUIRED, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, type EnsureWorkspaceSandboxOptions, type LivenessProbeConfig, type MemberSyncSeam, type ModelSelection, type ModelSelectionError, type ModelSelectionFailure, type ModelSelectionSource, type Outcome, PREWARM_CLAIM_TABLE_DDL, PROVISION_PAYLOAD_MAX_BYTES, PYPI_EGRESS_DOMAINS, type PeekWorkspaceSandboxOutcome, type PrewarmClaimD1Like, type PrewarmClaimStore, type PrewarmDecision, type PrewarmEvent, type PrewarmOutcome, type PrewarmResult, type ProfileComposeOptions, type PromptInputPart, type ProviderResolutionConfig, type ProvisionPayloadSections, type ProvisionProfileSection, type ResolveSandboxClientCredentialsOptions, type ResolvedModel, type SafeSandboxErrorCause, type SafeSandboxErrorDiagnostics, type SandboxBuildContext, type SandboxClientCredentials, type SandboxCredentialEnvironment, SandboxEgressPolicyMismatchError, type SandboxEgressPolicySource, type SandboxExecChannel, type SandboxExecOptions, type SandboxExistingBoxStage, type SandboxFileBytesOutcome, type SandboxFileSizeOutcome, SandboxModelResolutionError, type SandboxPermissionLevel, type SandboxPrewarmScope, type SandboxPrewarmer, type SandboxPrewarmerOptions, type SandboxReadiness, SandboxRecoveryFailedError, type SandboxRecoveryPhase, type SandboxResourceConfig, type SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, type SandboxRuntimeConfig, type SandboxScope, type SandboxStepTransition, type SandboxStreamEvent, type SandboxTerminalConnectionRouteOptions, type SandboxToolPathOptions, type SandboxToolSpec, type ScopedTokenResult, type SecretStore, type StoppedSandboxResumeFailure, type StoppedSandboxResumeRecovery, type StreamSandboxPromptOptions, type TerminalConnectionBoxLike, WORKSPACE_SANDBOX_HOST_EXHAUSTED, WORKSPACE_SANDBOX_MISSING, WORKSPACE_SANDBOX_RECOVERY_ACTIONS, WORKSPACE_SANDBOX_RECOVERY_CODES, WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS, WORKSPACE_SANDBOX_UNRECOVERABLE, type WorkspaceSandboxEnsureContext, type WorkspaceSandboxInstanceLike, type WorkspaceSandboxManager, type WorkspaceSandboxManagerOptions, type WorkspaceSandboxRecoveryAction, type WorkspaceSandboxRecoveryCode, type WorkspaceSandboxRecoveryDecision, type WorkspaceSandboxRecoveryManager, WorkspaceSandboxRecoveryRequiredError, type WorkspaceSandboxRecoveryState, type WorkspaceSandboxRecoveryStore, type WorkspaceSandboxSnapshot, type WorkspaceSandboxSnapshotAssessment, type WorkspaceSandboxSnapshotAvailability, type WorkspaceSandboxSnapshotFreshness, type WriteProfileFilesOptions, adaptSandboxStream, assertEnvWithinLimits, assertProvisionPayloadWithinCap, assessWorkspaceSandboxSnapshot, attachReasoningEffort, buildAppToolMcpServers, buildProductEgressPolicy, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, collectSandboxPromptText, createD1PrewarmClaimStore, createSandboxPrewarmer, createSandboxTerminalConnectionRoute, createWorkspaceSandboxManager, createWorkspaceSandboxRecoveryManager, deferredCorpusHash, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, ensureWorkspaceSandbox, flattenHistory, formatSandboxProvisioningSupportDetails, formatSandboxProvisioningUserMessage, getClient, isEgressProxyRecoveryRequiredError, isSandboxApiBearerAuthFailure, isSandboxApiSandboxMissingFailure, isSandboxAuthFailure, isSandboxHostCapacityFailure, isTerminalPromptEvent, isWorkspaceSandboxRecoveryAction, isWorkspaceSandboxRecoveryCode, isWorkspaceSandboxRecoveryState, isWorkspaceSandboxSnapshotRestoreError, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, peekWorkspaceSandbox, preferredWorkspaceSandboxRecoveryBoxKey, readSandboxBinaryBytes, readSecret, requireTransportableModel, resetClientCache, resolveModel, resolveModelSelection, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, serializeSandboxProvisioningError, shellQuote, shouldRestoreWorkspaceSandboxRecovery, splitDeferredProfileFiles, statSandboxFileSize, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, workspaceSandboxRecoveryDiagnostic, workspaceSandboxRecoveryFromError, workspaceSandboxRecoveryMessage, workspaceSandboxRecoveryRecommendedActions, writeProfileFilesToBox };
1660
+ export { type AppToolDescriptor, type BuildAppToolMcpServersOptions, type BuildSandboxToolFileMountsOptions, type D1PrewarmClaimStoreOptions, DEFAULT_PREWARM_CLAIM_TABLE, DEFAULT_PROVISION_TIMEOUT_MS, DEFAULT_SANDBOX_RESOURCES, DEFAULT_SIDECAR_PROCESS_PATTERN, type DriveSandboxTurnOptions, EGRESS_PROXY_RECOVERY_PHASE, EGRESS_PROXY_RECOVERY_REQUIRED, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, type EnsureWorkspaceSandboxOptions, type LivenessProbeConfig, type MemberSyncSeam, type ModelSelection, type ModelSelectionError, type ModelSelectionFailure, type ModelSelectionSource, type Outcome, PREWARM_CLAIM_TABLE_DDL, PROVISION_PAYLOAD_MAX_BYTES, PYPI_EGRESS_DOMAINS, type PeekWorkspaceSandboxOutcome, type PrewarmClaimD1Like, type PrewarmClaimStore, type PrewarmDecision, type PrewarmEvent, type PrewarmOutcome, type PrewarmResult, type ProfileComposeOptions, type PromptInputPart, type ProviderResolutionConfig, type ProvisionPayloadSections, type ProvisionProfileSection, type ResolveSandboxClientCredentialsOptions, type ResolvedModel, type SafeSandboxErrorCause, type SafeSandboxErrorDiagnostics, type SandboxBuildContext, type SandboxClientCredentials, type SandboxCredentialEnvironment, SandboxEgressPolicyMismatchError, type SandboxEgressPolicySource, type SandboxExecChannel, type SandboxExecOptions, type SandboxExistingBoxStage, type SandboxFileBytesOutcome, type SandboxFileSizeOutcome, SandboxModelResolutionError, type SandboxPermissionLevel, type SandboxPrewarmScope, type SandboxPrewarmer, type SandboxPrewarmerOptions, type SandboxProvisionedObservation, type SandboxReadiness, SandboxRecoveryFailedError, type SandboxRecoveryPhase, type SandboxResourceConfig, type SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, type SandboxRuntimeConfig, type SandboxScope, type SandboxSpendHooks, type SandboxStepTransition, type SandboxStreamEvent, type SandboxTerminalConnectionRouteOptions, type SandboxToolPathOptions, type SandboxToolSpec, type ScopedTokenResult, type SecretStore, type StoppedSandboxResumeFailure, type StoppedSandboxResumeRecovery, type StreamSandboxPromptOptions, type TerminalConnectionBoxLike, WORKSPACE_SANDBOX_HOST_EXHAUSTED, WORKSPACE_SANDBOX_MISSING, WORKSPACE_SANDBOX_RECOVERY_ACTIONS, WORKSPACE_SANDBOX_RECOVERY_CODES, WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS, WORKSPACE_SANDBOX_UNRECOVERABLE, type WorkspaceSandboxEnsureContext, type WorkspaceSandboxInstanceLike, type WorkspaceSandboxManager, type WorkspaceSandboxManagerOptions, type WorkspaceSandboxRecoveryAction, type WorkspaceSandboxRecoveryCode, type WorkspaceSandboxRecoveryDecision, type WorkspaceSandboxRecoveryManager, WorkspaceSandboxRecoveryRequiredError, type WorkspaceSandboxRecoveryState, type WorkspaceSandboxRecoveryStore, type WorkspaceSandboxSnapshot, type WorkspaceSandboxSnapshotAssessment, type WorkspaceSandboxSnapshotAvailability, type WorkspaceSandboxSnapshotFreshness, type WriteProfileFilesOptions, adaptSandboxStream, assertEnvWithinLimits, assertProvisionPayloadWithinCap, assessWorkspaceSandboxSnapshot, attachReasoningEffort, buildAppToolMcpServers, buildProductEgressPolicy, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, collectSandboxPromptText, createD1PrewarmClaimStore, createSandboxPrewarmer, createSandboxTerminalConnectionRoute, createWorkspaceSandboxManager, createWorkspaceSandboxRecoveryManager, deferredCorpusHash, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, ensureWorkspaceSandbox, flattenHistory, formatSandboxProvisioningSupportDetails, formatSandboxProvisioningUserMessage, getClient, isEgressProxyRecoveryRequiredError, isSandboxApiBearerAuthFailure, isSandboxApiSandboxMissingFailure, isSandboxAuthFailure, isSandboxHostCapacityFailure, isTerminalPromptEvent, isWorkspaceSandboxRecoveryAction, isWorkspaceSandboxRecoveryCode, isWorkspaceSandboxRecoveryState, isWorkspaceSandboxSnapshotRestoreError, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, peekWorkspaceSandbox, preferredWorkspaceSandboxRecoveryBoxKey, readSandboxBinaryBytes, readSecret, requireTransportableModel, resetClientCache, resolveModel, resolveModelSelection, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, serializeSandboxProvisioningError, shellQuote, shouldRestoreWorkspaceSandboxRecovery, splitDeferredProfileFiles, statSandboxFileSize, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, workspaceSandboxRecoveryDiagnostic, workspaceSandboxRecoveryFromError, workspaceSandboxRecoveryMessage, workspaceSandboxRecoveryRecommendedActions, writeProfileFilesToBox };
@@ -89,7 +89,7 @@ import {
89
89
  workspaceSandboxRecoveryMessage,
90
90
  workspaceSandboxRecoveryRecommendedActions,
91
91
  writeProfileFilesToBox
92
- } from "../chunk-7V2I3JGZ.js";
92
+ } from "../chunk-OTLEYHOG.js";
93
93
  import "../chunk-TH7L265V.js";
94
94
  import "../chunk-LWSJK546.js";
95
95
  import "../chunk-P5PIAQVS.js";
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ formatSpendReport,
4
+ reconcileSpend,
5
+ spendReportToJson
6
+ } from "../chunk-NEJ7OMQS.js";
7
+
8
+ // src/spend/cli-args.ts
9
+ import { pathToFileURL } from "url";
10
+ import { resolve } from "path";
11
+ var SpendUsageError = class extends Error {
12
+ };
13
+ var DEFAULT_CONFIG_FILE = "spend.config.mjs";
14
+ var USAGE = [
15
+ "agent-app-spend-check \u2014 reconcile settled sandbox compute against what this product asked for.",
16
+ "",
17
+ "Usage: agent-app-spend-check [--config <file>] [--json] [--as-of <iso>] [--skip <check,check>]",
18
+ "",
19
+ ` --config <file> Config module. Default ${DEFAULT_CONFIG_FILE}.`,
20
+ " --json Emit the report as JSON instead of a table.",
21
+ ' --as-of <iso> Treat this instant as "now". Default: the current time.',
22
+ " --skip <checks> Comma-separated check ids to leave out.",
23
+ "",
24
+ "The config module default-exports (or exports as `config`) the reconcile options,",
25
+ "or a function returning them. It supplies the two things this package cannot:",
26
+ "the product's own expectation store, and an authenticated fetch of its settled",
27
+ "ledger rows. Scope that fetch to boxes this product owns.",
28
+ "",
29
+ "Exit 0 = clean, 1 = findings, 2 = usage or config error."
30
+ ].join("\n");
31
+ function parseSpendArgs(argv) {
32
+ let configFile = DEFAULT_CONFIG_FILE;
33
+ let json = false;
34
+ let asOf;
35
+ let skip = [];
36
+ for (let i = 0; i < argv.length; i++) {
37
+ const arg = argv[i];
38
+ if (arg === "--json") {
39
+ json = true;
40
+ } else if (arg === "--config") {
41
+ const value = argv[++i];
42
+ if (!value) throw new SpendUsageError("--config needs a file path");
43
+ configFile = value;
44
+ } else if (arg === "--as-of") {
45
+ const value = argv[++i];
46
+ if (!value) throw new SpendUsageError("--as-of needs an ISO instant");
47
+ const parsed = Date.parse(value);
48
+ if (Number.isNaN(parsed)) throw new SpendUsageError(`--as-of is not a date: ${value}`);
49
+ asOf = parsed;
50
+ } else if (arg === "--skip") {
51
+ const value = argv[++i];
52
+ if (!value) throw new SpendUsageError("--skip needs a comma-separated list of check ids");
53
+ skip = value.split(",").map((part) => part.trim()).filter(Boolean);
54
+ } else if (arg === "--help" || arg === "-h") {
55
+ throw new SpendUsageError("help");
56
+ } else if (arg !== void 0) {
57
+ throw new SpendUsageError(`unrecognized argument: ${arg}`);
58
+ }
59
+ }
60
+ return { configFile, json, asOf, skip };
61
+ }
62
+ async function loadSpendConfig(configFile) {
63
+ const resolved = resolve(process.cwd(), configFile);
64
+ let module;
65
+ try {
66
+ module = await import(pathToFileURL(resolved).href);
67
+ } catch (err) {
68
+ throw new SpendUsageError(
69
+ `could not load ${resolved}: ${err instanceof Error ? err.message : String(err)}`
70
+ );
71
+ }
72
+ const exported = module.default ?? module.config;
73
+ const value = typeof exported === "function" ? await exported() : exported;
74
+ if (!value || typeof value !== "object") {
75
+ throw new SpendUsageError(`${resolved} must export reconcile options as \`default\` or \`config\``);
76
+ }
77
+ const options = value;
78
+ if (!Array.isArray(options.rows)) {
79
+ throw new SpendUsageError(`${resolved} must supply \`rows\` \u2014 the product's own settled-ledger fetch`);
80
+ }
81
+ if (!options.store) {
82
+ throw new SpendUsageError(`${resolved} must supply \`store\` \u2014 the product's expectation ledger`);
83
+ }
84
+ return options;
85
+ }
86
+
87
+ // src/spend/cli.ts
88
+ async function main() {
89
+ const args = parseSpendArgs(process.argv.slice(2));
90
+ const config = await loadSpendConfig(args.configFile);
91
+ const report = await reconcileSpend({
92
+ ...config,
93
+ ...args.asOf !== void 0 ? { asOf: args.asOf } : {},
94
+ ...args.skip.length > 0 ? { skip: args.skip } : {}
95
+ });
96
+ const rendered = args.json ? spendReportToJson(report) : formatSpendReport(report);
97
+ if (report.ok) process.stdout.write(`${rendered}
98
+ `);
99
+ else process.stderr.write(`${rendered}
100
+ `);
101
+ return report.ok ? 0 : 1;
102
+ }
103
+ try {
104
+ process.exit(await main());
105
+ } catch (err) {
106
+ if (err instanceof SpendUsageError) {
107
+ process.stderr.write(`${err.message === "help" ? "" : `${err.message}
108
+
109
+ `}${USAGE}
110
+ `);
111
+ process.exit(2);
112
+ }
113
+ process.stderr.write(
114
+ `agent-app-spend-check failed: ${err instanceof Error ? err.message : String(err)}
115
+ `
116
+ );
117
+ process.exit(2);
118
+ }
119
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/spend/cli-args.ts","../../src/spend/cli.ts"],"sourcesContent":["import { pathToFileURL } from 'node:url'\nimport { resolve } from 'node:path'\nimport type { ReconcileSpendOptions } from './reconcile'\n\n/** A usage or config problem — exit code 2, never a finding. */\nexport class SpendUsageError extends Error {}\n\nconst DEFAULT_CONFIG_FILE = 'spend.config.mjs'\n\nexport const USAGE = [\n 'agent-app-spend-check — reconcile settled sandbox compute against what this product asked for.',\n '',\n 'Usage: agent-app-spend-check [--config <file>] [--json] [--as-of <iso>] [--skip <check,check>]',\n '',\n ` --config <file> Config module. Default ${DEFAULT_CONFIG_FILE}.`,\n ' --json Emit the report as JSON instead of a table.',\n ' --as-of <iso> Treat this instant as \"now\". Default: the current time.',\n ' --skip <checks> Comma-separated check ids to leave out.',\n '',\n 'The config module default-exports (or exports as `config`) the reconcile options,',\n 'or a function returning them. It supplies the two things this package cannot:',\n 'the product\\'s own expectation store, and an authenticated fetch of its settled',\n 'ledger rows. Scope that fetch to boxes this product owns.',\n '',\n 'Exit 0 = clean, 1 = findings, 2 = usage or config error.',\n].join('\\n')\n\nexport interface SpendCliArgs {\n readonly configFile: string\n readonly json: boolean\n readonly asOf: number | undefined\n readonly skip: readonly string[]\n}\n\n/** Parse argv. Split out of `cli.ts` so it is testable without a `process.exit`. */\nexport function parseSpendArgs(argv: readonly string[]): SpendCliArgs {\n let configFile = DEFAULT_CONFIG_FILE\n let json = false\n let asOf: number | undefined\n let skip: string[] = []\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n if (arg === '--json') {\n json = true\n } else if (arg === '--config') {\n const value = argv[++i]\n if (!value) throw new SpendUsageError('--config needs a file path')\n configFile = value\n } else if (arg === '--as-of') {\n const value = argv[++i]\n if (!value) throw new SpendUsageError('--as-of needs an ISO instant')\n const parsed = Date.parse(value)\n if (Number.isNaN(parsed)) throw new SpendUsageError(`--as-of is not a date: ${value}`)\n asOf = parsed\n } else if (arg === '--skip') {\n const value = argv[++i]\n if (!value) throw new SpendUsageError('--skip needs a comma-separated list of check ids')\n skip = value.split(',').map((part) => part.trim()).filter(Boolean)\n } else if (arg === '--help' || arg === '-h') {\n throw new SpendUsageError('help')\n } else if (arg !== undefined) {\n throw new SpendUsageError(`unrecognized argument: ${arg}`)\n }\n }\n\n return { configFile, json, asOf, skip }\n}\n\n/**\n * Load the product's reconcile options.\n *\n * A function export is awaited, so a config can open its database connection and\n * mint its platform credential at load time rather than at module scope.\n */\nexport async function loadSpendConfig(configFile: string): Promise<ReconcileSpendOptions> {\n const resolved = resolve(process.cwd(), configFile)\n let module: Record<string, unknown>\n try {\n module = (await import(pathToFileURL(resolved).href)) as Record<string, unknown>\n } catch (err) {\n throw new SpendUsageError(\n `could not load ${resolved}: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n const exported = module.default ?? module.config\n const value = typeof exported === 'function' ? await (exported as () => unknown)() : exported\n if (!value || typeof value !== 'object') {\n throw new SpendUsageError(`${resolved} must export reconcile options as \\`default\\` or \\`config\\``)\n }\n const options = value as Partial<ReconcileSpendOptions>\n if (!Array.isArray(options.rows)) {\n throw new SpendUsageError(`${resolved} must supply \\`rows\\` — the product's own settled-ledger fetch`)\n }\n if (!options.store) {\n throw new SpendUsageError(`${resolved} must supply \\`store\\` — the product's expectation ledger`)\n }\n return options as ReconcileSpendOptions\n}\n","#!/usr/bin/env node\n/**\n * `agent-app-spend-check` — reconcile what the platform charged for sandbox\n * compute against what this product believes it asked for.\n *\n * Runs in the CONSUMER's repo, on a schedule, over the consumer's own ledger\n * fetch and expectation store — the two things this package deliberately does\n * not reach for. Add it next to the other gates:\n *\n * \"scripts\": { \"spend-check\": \"agent-app-spend-check\" }\n *\n * Exits 1 on any finding, 0 when clean, 2 on a usage or config error. It never\n * disputes, refunds, or writes anything; the output is evidence a human acts on.\n */\nimport { SpendUsageError, USAGE, loadSpendConfig, parseSpendArgs } from './cli-args'\nimport { reconcileSpend } from './reconcile'\nimport { formatSpendReport, spendReportToJson } from './report'\nimport type { SpendCheckId } from './types'\n\nasync function main(): Promise<number> {\n const args = parseSpendArgs(process.argv.slice(2))\n const config = await loadSpendConfig(args.configFile)\n const report = await reconcileSpend({\n ...config,\n ...(args.asOf !== undefined ? { asOf: args.asOf } : {}),\n ...(args.skip.length > 0 ? { skip: args.skip as SpendCheckId[] } : {}),\n })\n const rendered = args.json ? spendReportToJson(report) : formatSpendReport(report)\n // A clean report is routine output; a failing one belongs on stderr so a cron\n // that only forwards stderr still delivers the alert.\n if (report.ok) process.stdout.write(`${rendered}\\n`)\n else process.stderr.write(`${rendered}\\n`)\n return report.ok ? 0 : 1\n}\n\ntry {\n process.exit(await main())\n} catch (err) {\n if (err instanceof SpendUsageError) {\n process.stderr.write(`${err.message === 'help' ? '' : `${err.message}\\n\\n`}${USAGE}\\n`)\n process.exit(2)\n }\n process.stderr.write(\n `agent-app-spend-check failed: ${err instanceof Error ? err.message : String(err)}\\n`,\n )\n process.exit(2)\n}\n"],"mappings":"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","names":[]}