@open-everywhere/adapters 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +21 -0
  3. package/README.md +43 -0
  4. package/agent/aws/cli.ts +121 -0
  5. package/agent/aws/config.ts +71 -0
  6. package/agent/aws/register.ts +65 -0
  7. package/agent/aws/sso.ts +567 -0
  8. package/agent/aws/sts.ts +191 -0
  9. package/agent/blob/hosting.ts +24 -0
  10. package/agent/browser/config.ts +62 -0
  11. package/agent/browser/register.ts +66 -0
  12. package/agent/browser/sandbox.ts +39 -0
  13. package/agent/contributions.ts +12 -0
  14. package/agent/directory/config.ts +138 -0
  15. package/agent/directory/okta.ts +226 -0
  16. package/agent/directory/register.ts +16 -0
  17. package/agent/instrumentation.ts +42 -0
  18. package/agent/model/approval-judge.ts +57 -0
  19. package/agent/model/dream-judge.ts +105 -0
  20. package/agent/model/gateway-vision.ts +162 -0
  21. package/agent/model/gateway.ts +85 -0
  22. package/agent/oe-oauth/config.ts +246 -0
  23. package/agent/oe-oauth/errors.ts +77 -0
  24. package/agent/oe-oauth/oauth-client.ts +376 -0
  25. package/agent/oe-oauth/pkce.ts +21 -0
  26. package/agent/oe-oauth/register.ts +32 -0
  27. package/agent/oe-oauth/token-vault.ts +652 -0
  28. package/agent/oe-oauth/user-auth-flow.ts +76 -0
  29. package/agent/postgres/neon.ts +108 -0
  30. package/agent/sandbox/backend-stop.ts +259 -0
  31. package/agent/sandbox/created-sandbox-registry.ts +27 -0
  32. package/agent/sandbox/extend-workspace-time.ts +77 -0
  33. package/agent/sandbox/vercel-lifecycle.ts +252 -0
  34. package/dist/agent/aws/cli.d.ts +24 -0
  35. package/dist/agent/aws/cli.d.ts.map +1 -0
  36. package/dist/agent/aws/cli.js +89 -0
  37. package/dist/agent/aws/cli.js.map +1 -0
  38. package/dist/agent/aws/config.d.ts +27 -0
  39. package/dist/agent/aws/config.d.ts.map +1 -0
  40. package/dist/agent/aws/config.js +46 -0
  41. package/dist/agent/aws/config.js.map +1 -0
  42. package/dist/agent/aws/register.d.ts +23 -0
  43. package/dist/agent/aws/register.d.ts.map +1 -0
  44. package/dist/agent/aws/register.js +39 -0
  45. package/dist/agent/aws/register.js.map +1 -0
  46. package/dist/agent/aws/sso.d.ts +109 -0
  47. package/dist/agent/aws/sso.d.ts.map +1 -0
  48. package/dist/agent/aws/sso.js +421 -0
  49. package/dist/agent/aws/sso.js.map +1 -0
  50. package/dist/agent/aws/sts.d.ts +27 -0
  51. package/dist/agent/aws/sts.d.ts.map +1 -0
  52. package/dist/agent/aws/sts.js +135 -0
  53. package/dist/agent/aws/sts.js.map +1 -0
  54. package/dist/agent/blob/hosting.d.ts +4 -0
  55. package/dist/agent/blob/hosting.d.ts.map +1 -0
  56. package/dist/agent/blob/hosting.js +20 -0
  57. package/dist/agent/blob/hosting.js.map +1 -0
  58. package/dist/agent/browser/config.d.ts +24 -0
  59. package/dist/agent/browser/config.d.ts.map +1 -0
  60. package/dist/agent/browser/config.js +50 -0
  61. package/dist/agent/browser/config.js.map +1 -0
  62. package/dist/agent/browser/register.d.ts +7 -0
  63. package/dist/agent/browser/register.d.ts.map +1 -0
  64. package/dist/agent/browser/register.js +47 -0
  65. package/dist/agent/browser/register.js.map +1 -0
  66. package/dist/agent/browser/sandbox.d.ts +23 -0
  67. package/dist/agent/browser/sandbox.d.ts.map +1 -0
  68. package/dist/agent/browser/sandbox.js +27 -0
  69. package/dist/agent/browser/sandbox.js.map +1 -0
  70. package/dist/agent/contributions.d.ts +16 -0
  71. package/dist/agent/contributions.d.ts.map +1 -0
  72. package/dist/agent/contributions.js +11 -0
  73. package/dist/agent/contributions.js.map +1 -0
  74. package/dist/agent/directory/config.d.ts +39 -0
  75. package/dist/agent/directory/config.d.ts.map +1 -0
  76. package/dist/agent/directory/config.js +101 -0
  77. package/dist/agent/directory/config.js.map +1 -0
  78. package/dist/agent/directory/okta.d.ts +19 -0
  79. package/dist/agent/directory/okta.d.ts.map +1 -0
  80. package/dist/agent/directory/okta.js +178 -0
  81. package/dist/agent/directory/okta.js.map +1 -0
  82. package/dist/agent/directory/register.d.ts +6 -0
  83. package/dist/agent/directory/register.d.ts.map +1 -0
  84. package/dist/agent/directory/register.js +16 -0
  85. package/dist/agent/directory/register.js.map +1 -0
  86. package/dist/agent/instrumentation.d.ts +5 -0
  87. package/dist/agent/instrumentation.d.ts.map +1 -0
  88. package/dist/agent/instrumentation.js +39 -0
  89. package/dist/agent/instrumentation.js.map +1 -0
  90. package/dist/agent/model/approval-judge.d.ts +15 -0
  91. package/dist/agent/model/approval-judge.d.ts.map +1 -0
  92. package/dist/agent/model/approval-judge.js +46 -0
  93. package/dist/agent/model/approval-judge.js.map +1 -0
  94. package/dist/agent/model/dream-judge.d.ts +40 -0
  95. package/dist/agent/model/dream-judge.d.ts.map +1 -0
  96. package/dist/agent/model/dream-judge.js +80 -0
  97. package/dist/agent/model/dream-judge.js.map +1 -0
  98. package/dist/agent/model/gateway-vision.d.ts +53 -0
  99. package/dist/agent/model/gateway-vision.d.ts.map +1 -0
  100. package/dist/agent/model/gateway-vision.js +129 -0
  101. package/dist/agent/model/gateway-vision.js.map +1 -0
  102. package/dist/agent/model/gateway.d.ts +30 -0
  103. package/dist/agent/model/gateway.d.ts.map +1 -0
  104. package/dist/agent/model/gateway.js +55 -0
  105. package/dist/agent/model/gateway.js.map +1 -0
  106. package/dist/agent/oe-oauth/config.d.ts +69 -0
  107. package/dist/agent/oe-oauth/config.d.ts.map +1 -0
  108. package/dist/agent/oe-oauth/config.js +182 -0
  109. package/dist/agent/oe-oauth/config.js.map +1 -0
  110. package/dist/agent/oe-oauth/errors.d.ts +63 -0
  111. package/dist/agent/oe-oauth/errors.d.ts.map +1 -0
  112. package/dist/agent/oe-oauth/errors.js +73 -0
  113. package/dist/agent/oe-oauth/errors.js.map +1 -0
  114. package/dist/agent/oe-oauth/oauth-client.d.ts +124 -0
  115. package/dist/agent/oe-oauth/oauth-client.d.ts.map +1 -0
  116. package/dist/agent/oe-oauth/oauth-client.js +262 -0
  117. package/dist/agent/oe-oauth/oauth-client.js.map +1 -0
  118. package/dist/agent/oe-oauth/pkce.d.ts +8 -0
  119. package/dist/agent/oe-oauth/pkce.d.ts.map +1 -0
  120. package/dist/agent/oe-oauth/pkce.js +18 -0
  121. package/dist/agent/oe-oauth/pkce.js.map +1 -0
  122. package/dist/agent/oe-oauth/register.d.ts +17 -0
  123. package/dist/agent/oe-oauth/register.d.ts.map +1 -0
  124. package/dist/agent/oe-oauth/register.js +24 -0
  125. package/dist/agent/oe-oauth/register.js.map +1 -0
  126. package/dist/agent/oe-oauth/token-vault.d.ts +107 -0
  127. package/dist/agent/oe-oauth/token-vault.d.ts.map +1 -0
  128. package/dist/agent/oe-oauth/token-vault.js +498 -0
  129. package/dist/agent/oe-oauth/token-vault.js.map +1 -0
  130. package/dist/agent/oe-oauth/user-auth-flow.d.ts +10 -0
  131. package/dist/agent/oe-oauth/user-auth-flow.d.ts.map +1 -0
  132. package/dist/agent/oe-oauth/user-auth-flow.js +66 -0
  133. package/dist/agent/oe-oauth/user-auth-flow.js.map +1 -0
  134. package/dist/agent/postgres/neon.d.ts +29 -0
  135. package/dist/agent/postgres/neon.d.ts.map +1 -0
  136. package/dist/agent/postgres/neon.js +76 -0
  137. package/dist/agent/postgres/neon.js.map +1 -0
  138. package/dist/agent/sandbox/backend-stop.d.ts +98 -0
  139. package/dist/agent/sandbox/backend-stop.d.ts.map +1 -0
  140. package/dist/agent/sandbox/backend-stop.js +188 -0
  141. package/dist/agent/sandbox/backend-stop.js.map +1 -0
  142. package/dist/agent/sandbox/created-sandbox-registry.d.ts +15 -0
  143. package/dist/agent/sandbox/created-sandbox-registry.d.ts.map +1 -0
  144. package/dist/agent/sandbox/created-sandbox-registry.js +26 -0
  145. package/dist/agent/sandbox/created-sandbox-registry.js.map +1 -0
  146. package/dist/agent/sandbox/extend-workspace-time.d.ts +31 -0
  147. package/dist/agent/sandbox/extend-workspace-time.d.ts.map +1 -0
  148. package/dist/agent/sandbox/extend-workspace-time.js +63 -0
  149. package/dist/agent/sandbox/extend-workspace-time.js.map +1 -0
  150. package/dist/agent/sandbox/vercel-lifecycle.d.ts +38 -0
  151. package/dist/agent/sandbox/vercel-lifecycle.d.ts.map +1 -0
  152. package/dist/agent/sandbox/vercel-lifecycle.js +244 -0
  153. package/dist/agent/sandbox/vercel-lifecycle.js.map +1 -0
  154. package/package.json +204 -0
  155. package/tsconfig.build.json +22 -0
  156. package/tsconfig.json +22 -0
@@ -0,0 +1,252 @@
1
+ // Stop-after-turn sandbox lifecycle — this file owns the DECISION, backend-stop.ts the mechanics. It exists
2
+ // because a session VM is billed for its whole idle tail; every failure is fail-soft to the `timeout` fuse.
3
+
4
+ import { defineHook, type HookContext } from "eve/hooks";
5
+ import { bindFrom, domainKeyOf, resolvePartition, appendUsageEvent, usageForSession, recordSpend, periodOf, estimateSandboxCostUsd, routineIdOf, turnSurfaceMode, getSettings, isSubagentChildContext, liveTaskIds, subagentParentSessionId, type BindableContext } from "@open-everywhere/core/agent/adapter-api.js";
6
+ import { resolveSandboxStopper, shouldStop, type SandboxListing, type SandboxStopper } from "./backend-stop.js";
7
+
8
+ // Re-exported so the one "is this sandbox up?" predicate and the one listing shape stay
9
+ // single-homed in backend-stop.ts while their existing import sites keep working
10
+ // (extend-workspace-time.ts, the lifecycle tests).
11
+ export { shouldStop, type SandboxListing, type SandboxStopper };
12
+
13
+ function log(msg: string): void {
14
+ console.log(`[open-everywhere:sandbox-lifecycle] ${msg}`);
15
+ }
16
+
17
+ /** On by default (the `sandbox.stopAfterTurn` setting, OPE-371); declare
18
+ * `sandbox: { stopAfterTurn: false }` in `agent/settings.ts`, or set the
19
+ * `sandbox.stopAfterTurn` ops override, to keep sandboxes alive between turns. */
20
+ export function stopAfterTurnEnabled(): boolean {
21
+ return getSettings().sandbox.stopAfterTurn;
22
+ }
23
+
24
+ /** One sandbox this sweep actually stopped, with its honestly-observed runtime window. */
25
+ export interface StoppedSandbox {
26
+ readonly name: string;
27
+ readonly sandboxSeconds: number;
28
+ }
29
+
30
+ /**
31
+ * Stop every running sandbox tagged with this session. Fail-soft per sandbox — a straggler falls back to the
32
+ * timeout fuse. The window is measured from `createdAt`, which does NOT advance across a stop and auto-resume,
33
+ * so this reports the CUMULATIVE window and the billing below takes the delta.
34
+ */
35
+ export async function stopSessionSandboxes(
36
+ sessionId: string,
37
+ stopper: SandboxStopper = resolveSandboxStopper(),
38
+ now: () => number = Date.now,
39
+ ): Promise<StoppedSandbox[]> {
40
+ const stopped: StoppedSandbox[] = [];
41
+ const sandboxes = await stopper.list(sessionId);
42
+ for (const s of sandboxes) {
43
+ if (!shouldStop(s.status)) continue;
44
+ try {
45
+ await stopper.stop(s.name);
46
+ const sandboxSeconds = Math.max(0, (now() - s.createdAt) / 1000);
47
+ stopped.push({ name: s.name, sandboxSeconds });
48
+ } catch (err) {
49
+ log(`stop of "${s.name}" failed (falls back to the timeout fuse): ${String(err)}`);
50
+ }
51
+ }
52
+ return stopped;
53
+ }
54
+
55
+ /**
56
+ * The second half of the child guard: hand the shared sandbox back to the parent, so the parent's own sweep
57
+ * finds it. A backend that never re-tags answers an empty result — a correct outcome, not a failure.
58
+ */
59
+ export async function restoreParentSandboxTag(ctx: HookContext, stopper: SandboxStopper): Promise<string[]> {
60
+ const parentSessionId = subagentParentSessionId(ctx as unknown as BindableContext);
61
+ // No typed linkage (eve does not expose `session.parent` in every child shape) ⇒ nothing to
62
+ // restore the tag TO. Skipping the stop is still correct and is the half that matters.
63
+ if (parentSessionId === undefined) return [];
64
+ const restored: string[] = [];
65
+ for (const s of await stopper.list(ctx.session.id)) {
66
+ try {
67
+ if (await stopper.retag(s.name, parentSessionId)) restored.push(s.name);
68
+ } catch (err) {
69
+ log(`re-tag of "${s.name}" back to parent session failed (the parent reclaims it on its next sandbox use): ${String(err)}`);
70
+ }
71
+ }
72
+ return restored;
73
+ }
74
+
75
+ /** Name the owning backend once per process, at the first turn end. An operator must be able to
76
+ * tell from the deployment which backend stops the sandbox — or that none does. */
77
+ let ownershipLogged = false;
78
+ function logStopOwnershipOnce(stopper: SandboxStopper): void {
79
+ if (ownershipLogged) return;
80
+ ownershipLogged = true;
81
+ log(stopper.ownership);
82
+ }
83
+
84
+ /** Test seam: forget that the ownership line was already logged. */
85
+ export function resetStopOwnershipLogForTest(): void {
86
+ ownershipLogged = false;
87
+ }
88
+
89
+ /**
90
+ * WHICH delegated background tasks this session believes still hold its sandbox. Non-empty ⇒ this turn end
91
+ * must not stop the VM. A read, never a write, and fail-soft to EMPTY.
92
+ */
93
+ export function liveDelegatedTasksHoldingSandbox(): readonly string[] {
94
+ try {
95
+ return liveTaskIds();
96
+ } catch {
97
+ return [];
98
+ }
99
+ }
100
+
101
+ async function onTurnEnd(ctx: HookContext, outcome: string): Promise<void> {
102
+ if (!stopAfterTurnEnabled()) return;
103
+ try {
104
+ const stopper = resolveSandboxStopper();
105
+ logStopOwnershipOnce(stopper);
106
+ // 🔴 OPE-447 — a delegated child shares the PARENT's sandbox and must not stop it.
107
+ if (isSubagentChildContext(ctx as unknown as BindableContext)) {
108
+ const restored = await restoreParentSandboxTag(ctx, stopper);
109
+ log(
110
+ `turn.${outcome}: SKIPPED the stop — this is an eve subagent child and the parent owns the shared sandbox` +
111
+ (restored.length > 0 ? `; handed ${restored.join(", ")} back to the parent session` : ""),
112
+ );
113
+ return;
114
+ }
115
+ // 🔴 OPE-595 — a live BACKGROUND child holds this session's VM after this turn ends.
116
+ // See the file header for the measurement and for why the skip cannot leak a VM.
117
+ const holding = liveDelegatedTasksHoldingSandbox();
118
+ if (holding.length > 0) {
119
+ log(
120
+ `turn.${outcome}: SKIPPED the stop — ${holding.length} delegated background task(s) still hold this session's sandbox ` +
121
+ `[${holding.join(", ")}]; the next turn end that finds none sweeps it`,
122
+ );
123
+ return;
124
+ }
125
+ const stopped = await stopSessionSandboxes(ctx.session.id, stopper);
126
+ if (stopped.length > 0) {
127
+ log(`turn.${outcome}: stopped ${stopped.map((s) => s.name).join(", ")} (auto-snapshot; next turn auto-resumes)`);
128
+ // Only a backend that bills runtime by wall clock gets a spend row. Pricing a self-hosted
129
+ // container at Vercel's GB-hour rate would invent a charge that the USD caps then enforce.
130
+ if (stopper.metered) await recordSandboxUsage(ctx, stopped);
131
+ }
132
+ } catch (err) {
133
+ // Listing failed (no credentials locally, transient API error, …) — the sandbox
134
+ // just idles out its timeout fuse, exactly the pre-hook behaviour.
135
+ log(`turn.${outcome}: sandbox stop skipped: ${String(err)}`);
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Record each stopped sandbox's runtime in BOTH spend stores, best-effort. ORDER IS LOAD-BEARING: the ledger
141
+ * row goes FIRST and the watermark only once it succeeds, so a failure re-bills instead of under-billing.
142
+ */
143
+ export async function recordSandboxUsage(ctx: HookContext, stopped: StoppedSandbox[]): Promise<void> {
144
+ try {
145
+ const p = resolvePartition(ctx);
146
+ // ATTRIBUTION (OPE-275) — the SAME derivations core's hooks/spend.ts stamps on the session's
147
+ // model rows: the ATTRIBUTED principal (a routine fire binds as its attested creator, a
148
+ // delegated child carries its parent's) and the dispatcher's routine marker. Before this the
149
+ // sandbox row wrote both as '' — so a person's delegated sandbox dollars were invisible to
150
+ // their `perUser` budget (surface/workspace caps caught them; the person axis did not), and a
151
+ // routine's reported cost excluded exactly its compute. Both reads are fail-soft: an unusual
152
+ // ctx degrades to an unattributed row, which is what the row always was.
153
+ const principalId = bindFrom(ctx).principalId;
154
+ const routineId = routineIdOf(ctx);
155
+ // OPE-1862 — the conversation's own mode, stamped on both rows below exactly as core's
156
+ // hooks/spend.ts stamps it on a model call. Without it a sandbox row is UNCLASSIFIED, and a
157
+ // report then masks the surface key it names rather than publish one it cannot classify.
158
+ const surfaceMode = turnSurfaceMode(ctx);
159
+ // The DELTA watermark (finding 4): the seconds already billed for each sandbox this session
160
+ // are the SUM of its prior "sandbox" usage_events deltas — the durable per-session stamp that
161
+ // survives a fresh serverless instance per turn. Read once; a failure leaves it empty, so the
162
+ // full cumulative is billed (today's overestimate), never dropped — the safe direction.
163
+ const priorBilled = new Map<string, number>();
164
+ try {
165
+ for (const e of await usageForSession(p.org, ctx.session.id)) {
166
+ if (e.kind !== "sandbox") continue;
167
+ const name = e.detail?.sandboxName;
168
+ if (typeof name !== "string") continue;
169
+ const secs = typeof e.detail?.sandboxSeconds === "number" ? e.detail.sandboxSeconds : 0;
170
+ priorBilled.set(name, (priorBilled.get(name) ?? 0) + secs);
171
+ }
172
+ } catch (err) {
173
+ log(`prior sandbox usage read skipped (billing the full window): ${String(err)}`);
174
+ }
175
+ // Dedupe by name: if `stopped` ever carries the same sandbox twice in one call (a
176
+ // list()/stop() quirk upstream), processing both entries would read the SAME
177
+ // priorBilled value for each and double-bill the delta. Keep the first occurrence.
178
+ const seenNames = new Set<string>();
179
+ const dedupedStopped = stopped.filter((s) => {
180
+ if (seenNames.has(s.name)) return false;
181
+ seenNames.add(s.name);
182
+ return true;
183
+ });
184
+ for (const s of dedupedStopped) {
185
+ // `s.sandboxSeconds` is the CUMULATIVE window since createdAt; bill only the increment
186
+ // since this sandbox's last stop (clamped ≥ 0: a sandbox recreated mid-session, finding 5,
187
+ // resets createdAt below the prior sum → 0 for that window, never negative).
188
+ const billedSeconds = Math.max(0, Math.round(s.sandboxSeconds) - (priorBilled.get(s.name) ?? 0));
189
+ // THE LEDGER WRITE FIRST (see the doc comment above for why the order flipped).
190
+ try {
191
+ await recordSpend(p, {
192
+ period: periodOf(new Date().toISOString()),
193
+ usage: {},
194
+ principalId,
195
+ routineId,
196
+ model: "",
197
+ ...(surfaceMode !== undefined ? { surfaceMode } : {}),
198
+ // Explicit usdMicros: runtime spend has no (model, tokens) key that could
199
+ // express it — this is the case that forced write-time pricing (OPE-265/266).
200
+ usdMicros: Math.round(estimateSandboxCostUsd(billedSeconds) * 1_000_000),
201
+ });
202
+ } catch (err) {
203
+ log(`sandbox spend-ledger record skipped for "${s.name}" (watermark NOT advanced — this window re-bills next stop): ${String(err)}`);
204
+ continue;
205
+ }
206
+ // The watermark: only reached once the ledger write above actually landed, so a
207
+ // future priorBilled sum never marks seconds "billed" that no ledger row backs.
208
+ try {
209
+ await appendUsageEvent({
210
+ org: p.org,
211
+ // SPEND grain (domainKeyOf), matching core's hooks/spend.ts: a Discord thread's
212
+ // sandbox seconds bill against its PARENT channel's scope (item 22); the domain
213
+ // key IS the surfaceKey everywhere else.
214
+ surfaceKey: domainKeyOf(p),
215
+ sessionId: ctx.session.id,
216
+ kind: "sandbox",
217
+ ...(surfaceMode !== undefined ? { surfaceMode } : {}),
218
+ principalId,
219
+ ...(routineId !== undefined ? { routineId } : {}),
220
+ inputTokens: 0,
221
+ outputTokens: 0,
222
+ cacheReadTokens: 0,
223
+ cacheWriteTokens: 0,
224
+ // The DELTA (not the cumulative) — so the next stop's watermark sum telescopes to the
225
+ // true lifetime, and a session_cost view that SUMS these rows is correct rather than
226
+ // over-counting (which the old cumulative-per-row write silently did).
227
+ detail: { sandboxSeconds: billedSeconds, sandboxName: s.name },
228
+ });
229
+ } catch (err) {
230
+ // The ledger row above already landed — only the watermark/forensics row is missing.
231
+ // A future stop's priorBilled sum under-counts by this delta, so it re-bills this
232
+ // window too: a double-bill, but over-billing is this file's chosen safe direction.
233
+ log(`sandbox usage-event append skipped for "${s.name}" (watermark not advanced; a future stop may re-bill this window): ${String(err)}`);
234
+ }
235
+ }
236
+ } catch (err) {
237
+ log(`sandbox usage records skipped (no resolvable partition): ${String(err)}`);
238
+ }
239
+ }
240
+
241
+ export default defineHook({
242
+ events: {
243
+ // Turn boundaries are the signal: the reply has been sent, nothing is using the VM.
244
+ "turn.completed": async (_event, ctx) => onTurnEnd(ctx, "completed"),
245
+ "turn.failed": async (_event, ctx) => onTurnEnd(ctx, "failed"),
246
+ // Completing sessions (routines, one-shots) end with session.* — a final sweep. If
247
+ // another session-end hook (memory persist) touches the sandbox after us it simply
248
+ // auto-resumes and the leftover falls back to the fuse: no correctness impact.
249
+ "session.completed": async (_event, ctx) => onTurnEnd(ctx, "session-completed"),
250
+ "session.failed": async (_event, ctx) => onTurnEnd(ctx, "session-failed"),
251
+ },
252
+ });
@@ -0,0 +1,24 @@
1
+ import type { AwsExecResult, AwsExecutor } from "@open-everywhere/core/agent/adapter-api.js";
2
+ export interface AwsExecInvocation {
3
+ /** The binary to launch (default "aws"). */
4
+ readonly file: string;
5
+ /** Argv AFTER the binary (creds are NEVER here). */
6
+ readonly args: readonly string[];
7
+ /** The child's full environment, with creds + region injected and ambient AWS identity stripped. */
8
+ readonly env: NodeJS.ProcessEnv;
9
+ }
10
+ /** A runner resolves with the captured outcome (ANY exit code) and REJECTS only on a launch failure —
11
+ * the DI seam the offline test injects to assert env/argv without running `aws`. */
12
+ export type AwsRunner = (inv: AwsExecInvocation) => Promise<AwsExecResult>;
13
+ /** Construction options — DI seams for the offline test; each has a real default. */
14
+ export interface AwsCliExecutorOptions {
15
+ /** The invocation runner. Default: a `node:child_process.execFile` wrapper. */
16
+ readonly run?: AwsRunner;
17
+ /** The AWS CLI binary name/path. Default "aws". */
18
+ readonly binary?: string;
19
+ /** Operator default region (OE_AWS_REGION) applied when the resolved identity names none, so the
20
+ * CLI runs in the same region STS minted for. */
21
+ readonly defaultRegion?: string;
22
+ }
23
+ export declare function makeAwsCliExecutor(opts?: AwsCliExecutorOptions): AwsExecutor;
24
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../agent/aws/cli.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAkC,aAAa,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAC;AAE7H,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,oGAAoG;IACpG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACjC;AAED;qFACqF;AACrF,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAE3E,qFAAqF;AACrF,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;sDACkD;IAClD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAoED,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,qBAA0B,GAAG,WAAW,CAwBhF"}
@@ -0,0 +1,89 @@
1
+ // The AWS-EXECUTOR adapter — the ONE place the `aws` binary is spawned. Credentials go in the child ENV,
2
+ // never on argv, and that env is DENY-BY-DEFAULT. A non-zero exit returns; only a launch failure throws.
3
+ import { execFile } from "node:child_process";
4
+ /** Default runner — `execFile` the CLI, capturing stdout/stderr/exit. Resolves for every exit code
5
+ * (non-zero included); rejects ONLY when the process could not be launched (ENOENT etc.). */
6
+ const defaultRunner = (inv) => new Promise((resolve, reject) => {
7
+ execFile(inv.file, [...inv.args], { env: inv.env, maxBuffer: 64 * 1024 * 1024 }, (err, stdout, stderr) => {
8
+ if (err) {
9
+ const code = err.code;
10
+ // execFile sets a NUMBER `code` for a non-zero exit, a STRING errno (e.g. "ENOENT") for a
11
+ // launch failure. Return the former; reject the latter.
12
+ if (typeof code === "number") {
13
+ resolve({ exitCode: code, stdout: String(stdout), stderr: String(stderr) });
14
+ return;
15
+ }
16
+ reject(err);
17
+ return;
18
+ }
19
+ resolve({ exitCode: 0, stdout: String(stdout), stderr: String(stderr) });
20
+ });
21
+ });
22
+ /**
23
+ * The ONLY parent-env keys the child inherits — deny-by-default. The framework process's environment holds
24
+ * the deployment's real secrets, and this child's argv is model-controlled.
25
+ */
26
+ const CHILD_ENV_PASSTHROUGH = [
27
+ "PATH",
28
+ "HOME",
29
+ "TMPDIR",
30
+ "TMP",
31
+ "TEMP",
32
+ "LANG",
33
+ "TZ",
34
+ "HTTPS_PROXY",
35
+ "HTTP_PROXY",
36
+ "NO_PROXY",
37
+ "https_proxy",
38
+ "http_proxy",
39
+ "no_proxy",
40
+ ];
41
+ /** Build the child env DENY-BY-DEFAULT: the enumerated pass-through + LC_* locale keys, the
42
+ * injected temp creds + region, config/credential files pinned to /dev/null, IMDS disabled —
43
+ * so the injected creds are the ONLY identity the CLI can resolve, from any source. */
44
+ function childEnv(credentials, region) {
45
+ const env = {};
46
+ for (const k of CHILD_ENV_PASSTHROUGH) {
47
+ if (process.env[k] !== undefined)
48
+ env[k] = process.env[k];
49
+ }
50
+ for (const k of Object.keys(process.env)) {
51
+ if (k.startsWith("LC_"))
52
+ env[k] = process.env[k];
53
+ }
54
+ // No on-host profile/credential/config file is ever readable, whatever the box carries.
55
+ env.AWS_CONFIG_FILE = "/dev/null";
56
+ env.AWS_SHARED_CREDENTIALS_FILE = "/dev/null";
57
+ // No instance-metadata fallback: a cloud host's IMDS role must never answer for the CLI.
58
+ env.AWS_EC2_METADATA_DISABLED = "true";
59
+ env.AWS_ACCESS_KEY_ID = credentials.accessKeyId;
60
+ env.AWS_SECRET_ACCESS_KEY = credentials.secretAccessKey;
61
+ env.AWS_SESSION_TOKEN = credentials.sessionToken;
62
+ if (region) {
63
+ env.AWS_REGION = region;
64
+ env.AWS_DEFAULT_REGION = region;
65
+ }
66
+ return env;
67
+ }
68
+ export function makeAwsCliExecutor(opts = {}) {
69
+ const runner = opts.run ?? defaultRunner;
70
+ const binary = opts.binary ?? "aws";
71
+ const defaultRegion = opts.defaultRegion;
72
+ return {
73
+ async run(req) {
74
+ const inv = {
75
+ file: binary,
76
+ args: req.args,
77
+ env: childEnv(req.credentials, req.region ?? defaultRegion),
78
+ };
79
+ try {
80
+ return await runner(inv);
81
+ }
82
+ catch (err) {
83
+ // A rejection here means the process never launched (the runner returns non-zero exits).
84
+ throw new Error(`failed to launch the AWS CLI ("${binary}") — is it installed and on PATH? ${err instanceof Error ? err.message : String(err)}`);
85
+ }
86
+ },
87
+ };
88
+ }
89
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../agent/aws/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /** Truthiness for the master flag — same off-spellings as the other connector-plane flags (default
2
+ * OFF: off is the inert posture; arming is a deliberate operator act). */
3
+ export declare function awsAccessEnabled(): boolean;
4
+ /** The AWS-access config block (env-sourced). Only a default region today; the identity/executor DI
5
+ * seams are passed by the caller (register.ts), not env. */
6
+ export interface AwsAccessConfig {
7
+ /** Default region applied when a role grant names none. STS itself further falls back to
8
+ * `AWS_REGION` then `us-east-1` (sts.ts), so this is purely the operator's override. */
9
+ readonly defaultRegion?: string;
10
+ }
11
+ /** Load the AWS-access config block from env. Always returns a (possibly empty) config — unlike the
12
+ * oe-oauth block there is no REQUIRED field, so the master flag alone gates arming. */
13
+ export declare function loadAwsAccessConfig(): AwsAccessConfig;
14
+ /** The Identity Center (SSO) link config block (env-sourced). Every field is optional here — the
15
+ * minter fails closed with an actionable message at LINK time if a piece the link flow needs (region,
16
+ * client id) is missing, so arming the master flag never requires SSO to be configured. */
17
+ export interface AwsSsoConfig {
18
+ /** Identity Center region for the OIDC + portal endpoints. Undefined ⇒ fall back to the default. */
19
+ readonly region?: string;
20
+ /** The public PKCE OIDC client id for the link flow. Undefined ⇒ the link flow fails closed loudly. */
21
+ readonly clientId?: string;
22
+ /** OAuth scopes requested on the link (default applied by the minter). */
23
+ readonly scopes?: readonly string[];
24
+ }
25
+ /** Load the Identity Center SSO link config from env. Region falls back to the general AWS default. */
26
+ export declare function loadAwsSsoConfig(): AwsSsoConfig;
27
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../agent/aws/config.ts"],"names":[],"mappings":"AAmBA;2EAC2E;AAC3E,wBAAgB,gBAAgB,IAAI,OAAO,CAK1C;AAED;6DAC6D;AAC7D,MAAM,WAAW,eAAe;IAC9B;6FACyF;IACzF,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;wFACwF;AACxF,wBAAgB,mBAAmB,IAAI,eAAe,CAGrD;AAED;;4FAE4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,oGAAoG;IACpG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,uGAAuG;IACvG,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,uGAAuG;AACvG,wBAAgB,gBAAgB,IAAI,YAAY,CAc/C"}
@@ -0,0 +1,46 @@
1
+ // The `aws` access master arming flag and config block. Flag-off ⇒ NOTHING registers and core's AWS paths
2
+ // fail closed, with no fallback. Config is env-sourced at the adapter boundary, like DATABASE_URL.
3
+ /** The master arming flag env var — one string this module's readers share. */
4
+ const OE_AWS_ENABLED_ENV = "OE_AWS_ENABLED";
5
+ /** Optional default region for STS + the CLI when a grant names none. */
6
+ const OE_AWS_REGION_ENV = "OE_AWS_REGION";
7
+ // The per-user Identity Center link config. These vars configure the LINK flow only — a mint needs the
8
+ // region and the stored token. NO secrets here: the OIDC client is a PUBLIC PKCE client.
9
+ /** The Identity Center region (where oidc.<region>.amazonaws.com + portal.sso.<region>.amazonaws.com
10
+ * live). Falls back to the general default region ({@link OE_AWS_REGION_ENV} / AWS_REGION). */
11
+ const OE_AWS_SSO_REGION_ENV = "OE_AWS_SSO_REGION";
12
+ /** The pre-registered PUBLIC OIDC client id for the Identity Center auth-code + PKCE link flow. */
13
+ const OE_AWS_SSO_CLIENT_ID_ENV = "OE_AWS_SSO_CLIENT_ID";
14
+ /** Optional space-separated OAuth scopes for the link (default "sso:account:access"). */
15
+ const OE_AWS_SSO_SCOPES_ENV = "OE_AWS_SSO_SCOPES";
16
+ /** Truthiness for the master flag — same off-spellings as the other connector-plane flags (default
17
+ * OFF: off is the inert posture; arming is a deliberate operator act). */
18
+ export function awsAccessEnabled() {
19
+ const raw = process.env[OE_AWS_ENABLED_ENV];
20
+ if (raw === undefined)
21
+ return false;
22
+ const v = raw.trim().toLowerCase();
23
+ return v !== "" && v !== "0" && v !== "false" && v !== "off" && v !== "no";
24
+ }
25
+ /** Load the AWS-access config block from env. Always returns a (possibly empty) config — unlike the
26
+ * oe-oauth block there is no REQUIRED field, so the master flag alone gates arming. */
27
+ export function loadAwsAccessConfig() {
28
+ const defaultRegion = process.env[OE_AWS_REGION_ENV]?.trim() || process.env.AWS_REGION?.trim();
29
+ return { ...(defaultRegion ? { defaultRegion } : {}) };
30
+ }
31
+ /** Load the Identity Center SSO link config from env. Region falls back to the general AWS default. */
32
+ export function loadAwsSsoConfig() {
33
+ const region = process.env[OE_AWS_SSO_REGION_ENV]?.trim() ||
34
+ process.env[OE_AWS_REGION_ENV]?.trim() ||
35
+ process.env.AWS_REGION?.trim();
36
+ const clientId = process.env[OE_AWS_SSO_CLIENT_ID_ENV]?.trim();
37
+ const scopes = process.env[OE_AWS_SSO_SCOPES_ENV]?.trim()
38
+ ? process.env[OE_AWS_SSO_SCOPES_ENV].trim().split(/\s+/u).filter(Boolean)
39
+ : undefined;
40
+ return {
41
+ ...(region ? { region } : {}),
42
+ ...(clientId ? { clientId } : {}),
43
+ ...(scopes && scopes.length ? { scopes } : {}),
44
+ };
45
+ }
46
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../agent/aws/config.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAC1G,mGAAmG;AAEnG,+EAA+E;AAC/E,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAC5C,yEAAyE;AACzE,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAE1C,uGAAuG;AACvG,yFAAyF;AAEzF;gGACgG;AAChG,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAClD,mGAAmG;AACnG,MAAM,wBAAwB,GAAG,sBAAsB,CAAC;AACxD,yFAAyF;AACzF,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAElD;2EAC2E;AAC3E,MAAM,UAAU,gBAAgB;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC5C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACpC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC;AAC7E,CAAC;AAUD;wFACwF;AACxF,MAAM,UAAU,mBAAmB;IACjC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;IAC/F,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACzD,CAAC;AAcD,uGAAuG;AACvG,MAAM,UAAU,gBAAgB;IAC9B,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,EAAE,IAAI,EAAE;QAC1C,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE;QACtC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE,IAAI,EAAE,CAAC;IAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,EAAE,IAAI,EAAE;QACvD,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QAC1E,CAAC,CAAC,SAAS,CAAC;IACd,OAAO;QACL,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type AwsIdentityProvider, type AwsExecutor } from "@open-everywhere/core/agent/adapter-api.js";
2
+ import { type AwsStsProviderOptions } from "./sts.js";
3
+ import { type AwsCliExecutorOptions } from "./cli.js";
4
+ import { type AwsSsoMinter, type AwsSsoMinterOptions } from "./sso.js";
5
+ /** Registration input — the DI seams for tests (the boot call passes none). */
6
+ export interface RegisterAwsAccessOptions {
7
+ /** STS identity-provider seams (injectable fetch + token-file reader). */
8
+ readonly identity?: AwsStsProviderOptions;
9
+ /** CLI executor seams (injectable runner + binary). */
10
+ readonly executor?: AwsCliExecutorOptions;
11
+ /** Identity Center (SSO) minter seams (injectable fetch + stores + config). */
12
+ readonly sso?: AwsSsoMinterOptions;
13
+ }
14
+ /**
15
+ * Construct the STS provider (with its delegated SSO minter), the `aws` CLI executor and the per-principal
16
+ * link store, and register all three for core lookup. Idempotent overwrite in the registries — last wins.
17
+ */
18
+ export declare function registerAwsAccess(opts?: RegisterAwsAccessOptions): {
19
+ provider: AwsIdentityProvider;
20
+ executor: AwsExecutor;
21
+ sso: AwsSsoMinter;
22
+ };
23
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../agent/aws/register.ts"],"names":[],"mappings":"AAGA,OAAO,EAML,KAAK,mBAAmB,EACxB,KAAK,WAAW,EACjB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAA8D,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGnI,+EAA+E;AAC/E,MAAM,WAAW,wBAAwB;IACvC,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IAC1C,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,GAAG,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,wBAA6B,GAAG;IACtE,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,EAAE,WAAW,CAAC;IACtB,GAAG,EAAE,YAAY,CAAC;CACnB,CA6BA"}
@@ -0,0 +1,39 @@
1
+ // Register the AWS-access layer, called only when the OE_AWS_ENABLED master flag is armed. Flag-off ⇒
2
+ // nothing registers and core's AWS mint, exec and link paths fail closed; rollback is not arming the flag.
3
+ import { registerAwsIdentityProvider, registerAwsExecutor, registerAwsSsoLinkStore, registerAwsSsoCallbackCompleter, registerCredentialPurgeHook, } from "@open-everywhere/core/agent/adapter-api.js";
4
+ import { makeAwsStsProvider } from "./sts.js";
5
+ import { makeAwsCliExecutor } from "./cli.js";
6
+ import { makeAwsSsoMinter, makeAwsSsoLinkStore, makeAwsSsoPurgeHook } from "./sso.js";
7
+ import { loadAwsAccessConfig, loadAwsSsoConfig } from "./config.js";
8
+ /**
9
+ * Construct the STS provider (with its delegated SSO minter), the `aws` CLI executor and the per-principal
10
+ * link store, and register all three for core lookup. Idempotent overwrite in the registries — last wins.
11
+ */
12
+ export function registerAwsAccess(opts = {}) {
13
+ // The operator's default region (OE_AWS_REGION / AWS_REGION) — applied when a role grant names none,
14
+ // consistently to both the STS mint and the CLI. Explicit test seams (opts.*) win over it.
15
+ const { defaultRegion } = loadAwsAccessConfig();
16
+ const ssoCfg = loadAwsSsoConfig();
17
+ // Phase 1: the per-principal Identity Center link store (vault-backed) + the SSO minter. Register the
18
+ // store on core's port FIRST so the minter's requireAwsSsoLinkStore() resolves it.
19
+ registerAwsSsoLinkStore(makeAwsSsoLinkStore());
20
+ // The purge hook registers ALONGSIDE the link store: a deployment that can mint per-person AWS credentials
21
+ // and cannot destroy them on offboarding is the state this closed. Registration is KEYED, so this ADDS.
22
+ registerCredentialPurgeHook("aws-sso", makeAwsSsoPurgeHook());
23
+ const sso = makeAwsSsoMinter({
24
+ ...(ssoCfg.region ? { region: ssoCfg.region } : defaultRegion ? { region: defaultRegion } : {}),
25
+ ...(ssoCfg.clientId ? { clientId: ssoCfg.clientId } : {}),
26
+ ...(ssoCfg.scopes ? { scopes: ssoCfg.scopes } : {}),
27
+ ...opts.sso,
28
+ });
29
+ // Wire the `GET OE_AWS_SSO_CALLBACK_PATH` route to the SSO minter's code→token exchange, mirroring the
30
+ // own-rolled OAuth adapter's registerOeOauthCallbackCompleter. Fires under the OE_AWS_ENABLED boot
31
+ // guard; flag-off ⇒ the completer is never registered and the route fails closed (generic error page).
32
+ registerAwsSsoCallbackCompleter({ complete: ({ state, code }) => sso.completeLink(state, code) });
33
+ const provider = makeAwsStsProvider({ defaultRegion, mintUserSso: (req) => sso.mint(req), ...opts.identity });
34
+ const executor = makeAwsCliExecutor({ defaultRegion, ...opts.executor });
35
+ registerAwsIdentityProvider(provider);
36
+ registerAwsExecutor(executor);
37
+ return { provider, executor, sso };
38
+ }
39
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../../agent/aws/register.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,2GAA2G;AAE3G,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,uBAAuB,EACvB,+BAA+B,EAC/B,2BAA2B,GAG5B,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAA8B,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAA8B,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAA+C,MAAM,UAAU,CAAC;AACnI,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAYpE;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAI,GAA6B,EAAE;IAKnE,qGAAqG;IACrG,2FAA2F;IAC3F,MAAM,EAAE,aAAa,EAAE,GAAG,mBAAmB,EAAE,CAAC;IAChD,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAElC,sGAAsG;IACtG,mFAAmF;IACnF,uBAAuB,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC/C,2GAA2G;IAC3G,wGAAwG;IACxG,2BAA2B,CAAC,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC9D,MAAM,GAAG,GAAG,gBAAgB,CAAC;QAC3B,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,GAAG,IAAI,CAAC,GAAG;KACZ,CAAC,CAAC;IAEH,uGAAuG;IACvG,mGAAmG;IACnG,uGAAuG;IACvG,+BAA+B,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAElG,MAAM,QAAQ,GAAG,kBAAkB,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9G,MAAM,QAAQ,GAAG,kBAAkB,CAAC,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzE,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IACtC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC9B,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;AACrC,CAAC"}