apps 0.0.1-beta.9 → 0.0.1-beta.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -38
- package/framework-reference.json +1 -1
- package/js/chunk-3LEVTFD4.js +712 -0
- package/js/chunk-5FHWNLCI.js +88 -0
- package/js/{chunk-FL2QR3XW.js → chunk-5MDA7MKL.js} +338 -73
- package/js/chunk-6XQLA5NC.js +5047 -0
- package/js/{chunk-M3B3DE4K.js → chunk-74YVXBKY.js} +463 -218
- package/js/chunk-7PHHWYV4.js +335 -0
- package/js/chunk-DADXWWRZ.js +449 -0
- package/js/{chunk-27F7W2LG.js → chunk-DDHBUKXX.js} +18 -2
- package/js/{chunk-UK573G7W.js → chunk-EOPQ727L.js} +87 -51
- package/js/{chunk-5T57N25Q.js → chunk-GQ33QSNB.js} +583 -69
- package/js/{chunk-JU7VHODU.js → chunk-H72MEDMN.js} +65 -10
- package/js/chunk-I5C7FD5Z.js +171 -0
- package/js/{chunk-CDL5PM2Q.js → chunk-IFJU5SYQ.js} +19082 -18402
- package/js/{chunk-VFZKSM3T.js → chunk-JPEUKEV4.js} +3 -3
- package/js/{chunk-KUMBKVJD.js → chunk-KLYSEHUD.js} +2 -2
- package/js/{chunk-RFVIONFX.js → chunk-KUCCHNVY.js} +123 -44
- package/js/chunk-OD3KFKZD.js +1617 -0
- package/js/{chunk-57RKNWL2.js → chunk-PD6I2SDG.js} +4 -4
- package/js/{chunk-72JNVHPK.js → chunk-PYQLOLOC.js} +4 -3
- package/js/{chunk-ODEIDRGW.js → chunk-QWQM3CLH.js} +795 -176
- package/js/{chunk-IJX4CYZO.js → chunk-TUMGL2OC.js} +228 -264
- package/js/{chunk-IPI4MFSN.js → chunk-XBG7HHXC.js} +2 -2
- package/js/chunk-XPRZTLHD.js +1915 -0
- package/js/chunk-YNJ5CZMJ.js +93 -0
- package/js/chunk-YRGL4QEF.js +49 -0
- package/js/{chunk-Z5M2LQOZ.js → chunk-YWFUWTUJ.js} +113 -58
- package/js/client.js +142 -78
- package/js/contracts.js +142 -21
- package/js/effect.js +18 -19
- package/js/{es-APDZKREY.js → es-DIULA3ZF.js} +62 -1659
- package/js/graphql.js +49 -40
- package/js/host.js +85 -62
- package/js/index.js +165 -70
- package/js/mcp/effect.js +21 -21
- package/js/mcp.js +76 -56
- package/js/openapi.js +4189 -1411
- package/js/react.js +2 -3
- package/js/skills/effect.js +6 -3
- package/js/skills.js +208 -81
- package/js/storage/facet.js +1 -540
- package/js/ui/auth/contracts.js +2 -2
- package/js/ui/auth.js +5 -5
- package/js/ui/contracts.js +5 -4
- package/js/ui/serving.js +8605 -225
- package/package.json +3 -8
- package/runtime.json +1 -1
- package/types/app-cache/src/contracts/cache.d.ts +18 -0
- package/types/app-cache/src/index.d.ts +34 -2
- package/types/apps/src/contracts/api-response-error.d.ts +4 -0
- package/types/apps/src/contracts/app.d.ts +60 -11
- package/types/apps/src/contracts/cache.d.ts +20 -0
- package/types/apps/src/contracts/context.d.ts +60 -17
- package/types/apps/src/contracts/elicitation.d.ts +23 -7
- package/types/apps/src/contracts/events.d.ts +94 -0
- package/types/apps/src/contracts/failure.d.ts +30 -1
- package/types/apps/src/contracts/graphql.d.ts +2 -1
- package/types/apps/src/contracts/host.d.ts +1446 -17
- package/types/apps/src/contracts/icons.d.ts +28 -0
- package/types/apps/src/contracts/live.d.ts +2 -0
- package/types/apps/src/contracts/mcp.d.ts +81 -20
- package/types/apps/src/contracts/network.d.ts +120 -0
- package/types/apps/src/contracts/openapi-compile.d.ts +82 -3
- package/types/apps/src/contracts/openapi.d.ts +41 -9
- package/types/apps/src/contracts/operations.d.ts +14 -7
- package/types/apps/src/contracts/placement.d.ts +102 -0
- package/types/apps/src/contracts/protocol-version.d.ts +2 -2
- package/types/apps/src/contracts/protocols/1.d.ts +733 -81
- package/types/apps/src/contracts/protocols/10.d.ts +1450 -0
- package/types/apps/src/contracts/protocols/11.d.ts +1149 -0
- package/types/apps/src/contracts/protocols/12.d.ts +3310 -0
- package/types/apps/src/contracts/protocols/2.d.ts +69 -26
- package/types/apps/src/contracts/protocols/3.d.ts +133 -33
- package/types/apps/src/contracts/protocols/4.d.ts +122 -45
- package/types/apps/src/contracts/protocols/5.d.ts +157 -47
- package/types/apps/src/contracts/protocols/6.d.ts +1629 -0
- package/types/apps/src/contracts/protocols/7.d.ts +1785 -0
- package/types/apps/src/contracts/protocols/8.d.ts +1182 -0
- package/types/apps/src/contracts/protocols/9.d.ts +1104 -0
- package/types/apps/src/contracts/protocols/current.d.ts +6669 -0
- package/types/apps/src/contracts/protocols/oauth-6.d.ts +74 -0
- package/types/apps/src/contracts/protocols/oauth.d.ts +134 -0
- package/types/apps/src/contracts/provider-error.d.ts +9 -0
- package/types/apps/src/contracts/provider.d.ts +90 -6
- package/types/apps/src/contracts/schedules.d.ts +7 -1
- package/types/apps/src/contracts/schema.d.ts +21 -0
- package/types/apps/src/contracts/skills.d.ts +50 -13
- package/types/apps/src/contracts/sql.d.ts +63 -0
- package/types/apps/src/contracts/ui.d.ts +44 -14
- package/types/apps/src/contracts/webhooks.d.ts +24 -7
- package/types/apps/src/contracts/workflows.d.ts +19 -17
- package/types/apps/src/facet.d.ts +4 -5
- package/types/apps/src/host.d.ts +5 -0
- package/types/apps/src/implementation/account-router.d.ts +4 -3
- package/types/apps/src/implementation/app.d.ts +41 -14
- package/types/apps/src/implementation/authoring.d.ts +25 -5
- package/types/apps/src/implementation/browser-scripts.gen.d.ts +6 -0
- package/types/apps/src/implementation/cache.d.ts +29 -2
- package/types/apps/src/implementation/catalog-cache.d.ts +35 -4
- package/types/apps/src/implementation/elicitation.d.ts +18 -15
- package/types/apps/src/implementation/events.d.ts +64 -0
- package/types/apps/src/implementation/failure-detail.d.ts +28 -5
- package/types/apps/src/implementation/graphql-catalog.d.ts +3 -3
- package/types/apps/src/implementation/graphql.d.ts +4 -3
- package/types/apps/src/implementation/input-problems.d.ts +6 -2
- package/types/apps/src/implementation/invocation-timing.d.ts +18 -0
- package/types/apps/src/implementation/live.d.ts +2 -2
- package/types/apps/src/implementation/mcp-call.d.ts +6 -5
- package/types/apps/src/implementation/mcp-catalog.d.ts +6 -4
- package/types/apps/src/implementation/mcp-client.d.ts +17 -9
- package/types/apps/src/implementation/mcp-tools.d.ts +2 -1
- package/types/apps/src/implementation/mcp.d.ts +20 -9
- package/types/apps/src/implementation/network.d.ts +16 -0
- package/types/apps/src/implementation/openapi-compile.d.ts +28 -7
- package/types/apps/src/implementation/openapi-document.d.ts +46 -19
- package/types/apps/src/implementation/openapi-names.d.ts +9 -4
- package/types/apps/src/implementation/openapi-request.d.ts +10 -2
- package/types/apps/src/implementation/openapi-source.d.ts +23 -1
- package/types/apps/src/implementation/openapi-upgrade.d.ts +4 -0
- package/types/apps/src/implementation/operations.d.ts +57 -7
- package/types/apps/src/implementation/optimistic.d.ts +1 -1
- package/types/apps/src/implementation/placement.d.ts +60 -0
- package/types/apps/src/implementation/protocol-operations.d.ts +3 -0
- package/types/apps/src/implementation/provider-error.d.ts +28 -1
- package/types/apps/src/implementation/provider.d.ts +19 -4
- package/types/apps/src/implementation/query-commit.d.ts +1 -1
- package/types/apps/src/implementation/router-catalog.d.ts +20 -18
- package/types/apps/src/implementation/router.d.ts +24 -5
- package/types/apps/src/implementation/runtime-yield.d.ts +7 -0
- package/types/apps/src/implementation/schedules.d.ts +39 -8
- package/types/apps/src/implementation/schema.d.ts +98 -11
- package/types/apps/src/implementation/skills.d.ts +18 -24
- package/types/apps/src/implementation/sql.d.ts +71 -0
- package/types/apps/src/implementation/ui-account-notice.d.ts +9 -0
- package/types/apps/src/implementation/ui-auth.d.ts +1 -1
- package/types/apps/src/implementation/ui-client.d.ts +2 -2
- package/types/apps/src/implementation/ui-document.d.ts +5 -0
- package/types/apps/src/implementation/ui-errors.d.ts +1 -1
- package/types/apps/src/implementation/ui-serving.d.ts +26 -9
- package/types/apps/src/implementation/ui-watch.d.ts +1 -1
- package/types/apps/src/implementation/upstream-error.d.ts +7 -0
- package/types/apps/src/implementation/webhooks.d.ts +15 -6
- package/types/apps/src/implementation/workflow-context.d.ts +6 -2
- package/types/apps/src/implementation/workflows.d.ts +11 -4
- package/types/apps/src/index.d.ts +68 -19
- package/types/apps/src/mcp.d.ts +20 -1
- package/types/apps/src/openapi.d.ts +6 -1
- package/types/apps/src/react.d.ts +1 -1
- package/types/apps/src/skills.d.ts +10 -8
- package/types/apps/src/ui-serving.d.ts +1 -1
- package/types/telemetry/src/app-records.d.ts +42 -0
- package/types/telemetry/src/browser-operations.d.ts +2 -2
- package/types/telemetry/src/browser.d.ts +8 -3
- package/types/telemetry/src/config.d.ts +2 -2
- package/types/telemetry/src/index.d.ts +2 -1
- package/types/telemetry/src/layer.d.ts +10 -4
- package/types/telemetry/src/measurements.d.ts +1 -1
- package/types/telemetry/src/ownership.d.ts +60 -0
- package/types/telemetry/src/recorded-failure.d.ts +29 -0
- package/types/telemetry/src/relay.d.ts +16 -6
- package/types/telemetry/src/span-attributes.d.ts +17 -1
- package/types/telemetry/src/transport.d.ts +8 -1
- package/types/utils/src/api-error.d.ts +55 -0
- package/types/utils/src/connection-failure.d.ts +33 -0
- package/types/utils/src/recorded-message.d.ts +18 -0
- package/types/utils/src/user-facing-error.d.ts +83 -0
- package/js/chunk-7EK2JDX4.js +0 -299
- package/js/chunk-IKS2C5MQ.js +0 -224
- package/js/chunk-MKWZPDR6.js +0 -63
- package/js/chunk-N7YDVXYJ.js +0 -186
- package/js/chunk-NKLWZJLE.js +0 -172
- package/js/chunk-PJJ24GU7.js +0 -877
- package/js/chunk-PZB6T6KU.js +0 -19
- package/js/chunk-R3EV5FTO.js +0 -97
- package/js/chunk-SJFZCGXG.js +0 -1243
- package/js/chunk-WLE7WX2U.js +0 -2645
- package/js/chunk-YXQHV65J.js +0 -242
- package/js/chunk-ZQINWV4W.js +0 -630
- package/js/mcp/stdio.js +0 -128
- package/types/app-data/src/implementation/cursor.d.ts +0 -10
- package/types/app-data/src/implementation/index-key.d.ts +0 -7
- package/types/app-data/src/implementation/promise.d.ts +0 -68
- package/types/app-data/src/implementation/range.d.ts +0 -8
- package/types/app-data/src/implementation/schema.d.ts +0 -18
- package/types/app-data/src/implementation/sqlite.d.ts +0 -11
- package/types/app-data/src/index.d.ts +0 -4
- package/types/apps/src/contracts/storage.d.ts +0 -68
- package/types/apps/src/implementation/mcp-stdio.d.ts +0 -4
- package/types/apps/src/implementation/storage.d.ts +0 -15
- package/types/apps/src/mcp-stdio.d.ts +0 -5
|
@@ -0,0 +1,1915 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authorCache,
|
|
3
|
+
unavailableCache
|
|
4
|
+
} from "./chunk-7PHHWYV4.js";
|
|
5
|
+
import {
|
|
6
|
+
accountSecrets,
|
|
7
|
+
boundFailureMessage,
|
|
8
|
+
describeFailure,
|
|
9
|
+
failureDetail,
|
|
10
|
+
leavingProviderError,
|
|
11
|
+
parseMcpError,
|
|
12
|
+
parseSkillLoadFailed,
|
|
13
|
+
readCatalog,
|
|
14
|
+
routerSkills
|
|
15
|
+
} from "./chunk-5MDA7MKL.js";
|
|
16
|
+
import {
|
|
17
|
+
appInvocationFetch
|
|
18
|
+
} from "./chunk-YRGL4QEF.js";
|
|
19
|
+
import {
|
|
20
|
+
folderSkillsEffect
|
|
21
|
+
} from "./chunk-XBG7HHXC.js";
|
|
22
|
+
import {
|
|
23
|
+
ResponseStatusError,
|
|
24
|
+
declaredEvents,
|
|
25
|
+
isApp,
|
|
26
|
+
makeEmitter,
|
|
27
|
+
toEffectApp,
|
|
28
|
+
withPlacements
|
|
29
|
+
} from "./chunk-DADXWWRZ.js";
|
|
30
|
+
import {
|
|
31
|
+
declaredOperations,
|
|
32
|
+
locate,
|
|
33
|
+
nativeOperation
|
|
34
|
+
} from "./chunk-YWFUWTUJ.js";
|
|
35
|
+
import {
|
|
36
|
+
accountProviderError,
|
|
37
|
+
appCode,
|
|
38
|
+
fromPromise,
|
|
39
|
+
httpProviderError,
|
|
40
|
+
parseProviderError,
|
|
41
|
+
toPromise
|
|
42
|
+
} from "./chunk-I5C7FD5Z.js";
|
|
43
|
+
import {
|
|
44
|
+
AccountCheckResult,
|
|
45
|
+
DeclaredProvider,
|
|
46
|
+
DeclaredRequirements,
|
|
47
|
+
ElicitationFailed,
|
|
48
|
+
ElicitationResponse,
|
|
49
|
+
FormElicitation,
|
|
50
|
+
HostAccountsInvalid,
|
|
51
|
+
HostDeclarationInvalid,
|
|
52
|
+
HostError,
|
|
53
|
+
HostEvaluationFailed,
|
|
54
|
+
HostInputInvalid,
|
|
55
|
+
HostKindMismatch,
|
|
56
|
+
HostOperationFailed,
|
|
57
|
+
HostOperationNotFound,
|
|
58
|
+
HostOutputInvalid,
|
|
59
|
+
HostRequest,
|
|
60
|
+
HostRequestInvalid,
|
|
61
|
+
HostToolApprovalRequired,
|
|
62
|
+
HostToolBlocked,
|
|
63
|
+
HostToolNotFound,
|
|
64
|
+
HostToolPolicyFailed,
|
|
65
|
+
HostedRouter,
|
|
66
|
+
HostedTool,
|
|
67
|
+
HostedToolSummary,
|
|
68
|
+
HostedWebhook,
|
|
69
|
+
HostedWorkflow,
|
|
70
|
+
InvocationDeadline,
|
|
71
|
+
InvocationTimingSink,
|
|
72
|
+
ManualWebhookSetup,
|
|
73
|
+
ManyAccounts,
|
|
74
|
+
NonRetryableError,
|
|
75
|
+
OpenapiResponseError,
|
|
76
|
+
ResolvedAccounts,
|
|
77
|
+
ToolResultObservation,
|
|
78
|
+
TrustedToolApproval,
|
|
79
|
+
WebhookResponseData,
|
|
80
|
+
WorkflowDuration,
|
|
81
|
+
WorkflowFailure,
|
|
82
|
+
WorkflowName,
|
|
83
|
+
WorkflowReplay,
|
|
84
|
+
WorkflowStepOptions,
|
|
85
|
+
WorkflowValue,
|
|
86
|
+
approvalElicitation,
|
|
87
|
+
defaultWebhookTransportLimits,
|
|
88
|
+
maxInputProblems,
|
|
89
|
+
migrationsDirectory,
|
|
90
|
+
normalizePlacements,
|
|
91
|
+
reservedTablePrefix,
|
|
92
|
+
sqlBindingsLimit,
|
|
93
|
+
workflowDurationMillis
|
|
94
|
+
} from "./chunk-6XQLA5NC.js";
|
|
95
|
+
import {
|
|
96
|
+
AppSkills,
|
|
97
|
+
SkillFile
|
|
98
|
+
} from "./chunk-H72MEDMN.js";
|
|
99
|
+
import {
|
|
100
|
+
allowedValues,
|
|
101
|
+
echoableKey,
|
|
102
|
+
importedJsonSchema,
|
|
103
|
+
importedSchemaKeys,
|
|
104
|
+
jsonSchemaDecoder,
|
|
105
|
+
jsonSchemaDocument,
|
|
106
|
+
maxAlternativeKeys,
|
|
107
|
+
missingKey,
|
|
108
|
+
missingKeyText,
|
|
109
|
+
objectShape,
|
|
110
|
+
unionShape
|
|
111
|
+
} from "./chunk-GQ33QSNB.js";
|
|
112
|
+
import {
|
|
113
|
+
captureTelemetry,
|
|
114
|
+
makeOwnershipAccounting,
|
|
115
|
+
measuredSpan,
|
|
116
|
+
owned
|
|
117
|
+
} from "./chunk-QWQM3CLH.js";
|
|
118
|
+
import {
|
|
119
|
+
JsonObject,
|
|
120
|
+
JsonValue
|
|
121
|
+
} from "./chunk-DDHBUKXX.js";
|
|
122
|
+
import {
|
|
123
|
+
Base64_exports,
|
|
124
|
+
Cause_exports,
|
|
125
|
+
Clock_exports,
|
|
126
|
+
Effect_exports,
|
|
127
|
+
Match_exports,
|
|
128
|
+
Option_exports,
|
|
129
|
+
Redacted_exports,
|
|
130
|
+
SchemaAST_exports,
|
|
131
|
+
SchemaIssue_exports,
|
|
132
|
+
Schema_exports,
|
|
133
|
+
Stream_exports
|
|
134
|
+
} from "./chunk-IFJU5SYQ.js";
|
|
135
|
+
|
|
136
|
+
// src/implementation/invocation-timing.ts
|
|
137
|
+
var elicitationWaitSpan = "app.elicitation.wait";
|
|
138
|
+
var workflowControlSpan = "app.workflow.control";
|
|
139
|
+
var timedInvocation = (operation) => (effect) => Effect_exports.gen(function* () {
|
|
140
|
+
const report = yield* InvocationTimingSink;
|
|
141
|
+
return yield* effect.pipe(
|
|
142
|
+
measuredSpan(`app.${operation}`, {}, (start) => {
|
|
143
|
+
const accounting = makeOwnershipAccounting(start, "executor");
|
|
144
|
+
return {
|
|
145
|
+
around: accounting.observe,
|
|
146
|
+
end: (end, span) => {
|
|
147
|
+
const spent = accounting.read(end);
|
|
148
|
+
report({
|
|
149
|
+
elapsedMs: Number(end - start) / 1e6,
|
|
150
|
+
upstreamMs: spent.upstream,
|
|
151
|
+
elicitationMs: spent.person,
|
|
152
|
+
authoredMs: spent.app,
|
|
153
|
+
leaseMs: spent.lease,
|
|
154
|
+
backgroundMs: spent.background
|
|
155
|
+
});
|
|
156
|
+
span.attribute("executor.upstream.wait_ms", spent.upstream);
|
|
157
|
+
span.attribute("executor.elicitation.wait_ms", spent.person);
|
|
158
|
+
span.attribute("executor.authored_ms", spent.app);
|
|
159
|
+
span.attribute("executor.lease.wait_ms", spent.lease);
|
|
160
|
+
span.attribute("executor.background_ms", spent.background);
|
|
161
|
+
span.attribute("executor.overhead_ms", spent.executor);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// src/implementation/elicitation.ts
|
|
169
|
+
var prepareElicitation = (input) => Effect_exports.gen(function* () {
|
|
170
|
+
const request = yield* Schema_exports.decodeUnknownEffect(FormElicitation)(input, {
|
|
171
|
+
onExcessProperty: "error"
|
|
172
|
+
}).pipe(Effect_exports.mapError(() => new ElicitationFailed({ reason: "invalid-request" })));
|
|
173
|
+
const content = yield* jsonSchemaDecoder(request.requestedSchema).pipe(
|
|
174
|
+
Effect_exports.mapError(() => new ElicitationFailed({ reason: "invalid-request" }))
|
|
175
|
+
);
|
|
176
|
+
const respond = (input2) => Effect_exports.gen(function* () {
|
|
177
|
+
const response = yield* Schema_exports.decodeUnknownEffect(ElicitationResponse)(input2).pipe(
|
|
178
|
+
Effect_exports.catchTag(
|
|
179
|
+
"SchemaError",
|
|
180
|
+
() => Effect_exports.fail(new ElicitationFailed({ reason: "invalid-response" }))
|
|
181
|
+
)
|
|
182
|
+
);
|
|
183
|
+
if (response.action === "accept") {
|
|
184
|
+
const fields = response.content === void 0 ? {} : response.content;
|
|
185
|
+
yield* Schema_exports.decodeUnknownEffect(content)(fields).pipe(
|
|
186
|
+
Effect_exports.mapError(() => new ElicitationFailed({ reason: "invalid-response" }))
|
|
187
|
+
);
|
|
188
|
+
return { ...response, content: fields };
|
|
189
|
+
}
|
|
190
|
+
return response;
|
|
191
|
+
});
|
|
192
|
+
return { request, respond };
|
|
193
|
+
});
|
|
194
|
+
var makeElicit = (handler, signal, telemetry) => toPromise(
|
|
195
|
+
(input) => Effect_exports.gen(function* () {
|
|
196
|
+
if (handler === void 0) return yield* new ElicitationFailed({ reason: "unavailable" });
|
|
197
|
+
const form = yield* prepareElicitation(input);
|
|
198
|
+
return yield* handler(form.request, signal).pipe(
|
|
199
|
+
owned("person", elicitationWaitSpan),
|
|
200
|
+
Effect_exports.flatMap(form.respond)
|
|
201
|
+
);
|
|
202
|
+
}),
|
|
203
|
+
signal,
|
|
204
|
+
telemetry
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// src/contracts/approval.ts
|
|
208
|
+
var ApprovalDecision = Schema_exports.Literals(["approved", "denied", "user-approval"]);
|
|
209
|
+
|
|
210
|
+
// src/implementation/workflow-context.ts
|
|
211
|
+
var workflowSafe = (work, secrets) => work.pipe(
|
|
212
|
+
Effect_exports.catchCause((cause) => {
|
|
213
|
+
if (Cause_exports.hasInterrupts(cause)) return Effect_exports.interrupt;
|
|
214
|
+
const error = Cause_exports.squash(cause);
|
|
215
|
+
if (Schema_exports.is(WorkflowFailure)(error)) return Effect_exports.fail(error);
|
|
216
|
+
const { errorName, message } = failureDetail(error, secrets);
|
|
217
|
+
return Effect_exports.fail(
|
|
218
|
+
new WorkflowFailure({
|
|
219
|
+
reason: "execution",
|
|
220
|
+
retryable: !(error instanceof NonRetryableError),
|
|
221
|
+
...errorName === void 0 ? {} : { errorName },
|
|
222
|
+
...message === void 0 ? {} : { message }
|
|
223
|
+
})
|
|
224
|
+
);
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
var inStep = (step) => (failure) => failure.step === void 0 ? new WorkflowFailure({
|
|
228
|
+
reason: failure.reason,
|
|
229
|
+
retryable: failure.retryable,
|
|
230
|
+
step: step.slice(0, 200),
|
|
231
|
+
...failure.errorName === void 0 ? {} : { errorName: failure.errorName },
|
|
232
|
+
...failure.message.length === 0 ? {} : { message: failure.message }
|
|
233
|
+
}) : failure;
|
|
234
|
+
var makeWorkflowContext = (execution, definition, fresh, signal) => Effect_exports.gen(function* () {
|
|
235
|
+
const services = yield* Effect_exports.context();
|
|
236
|
+
const counts = /* @__PURE__ */ new Map();
|
|
237
|
+
const identify = (kind, name) => Effect_exports.gen(function* () {
|
|
238
|
+
yield* Schema_exports.decodeUnknownEffect(WorkflowName)(name).pipe(
|
|
239
|
+
Effect_exports.mapError(() => new WorkflowFailure({ reason: "input", retryable: false }))
|
|
240
|
+
);
|
|
241
|
+
const key = JSON.stringify([kind, name]);
|
|
242
|
+
const count = (counts.get(key) ?? 0) + 1;
|
|
243
|
+
counts.set(key, count);
|
|
244
|
+
const digest = yield* Effect_exports.tryPromise({
|
|
245
|
+
try: () => crypto.subtle.digest(
|
|
246
|
+
"SHA-256",
|
|
247
|
+
new TextEncoder().encode(JSON.stringify([execution.runId, kind, name, count]))
|
|
248
|
+
),
|
|
249
|
+
catch: () => new WorkflowFailure({ reason: "execution", retryable: false })
|
|
250
|
+
});
|
|
251
|
+
return Array.from(
|
|
252
|
+
new Uint8Array(digest),
|
|
253
|
+
(byte) => byte.toString(16).padStart(2, "0")
|
|
254
|
+
).join("");
|
|
255
|
+
});
|
|
256
|
+
const attempt = (options, work) => {
|
|
257
|
+
const timeout = workflowDurationMillis(options.timeout ?? "10 minutes");
|
|
258
|
+
if (timeout === void 0)
|
|
259
|
+
return Effect_exports.fail(new WorkflowFailure({ reason: "input", retryable: false }));
|
|
260
|
+
return work.pipe(
|
|
261
|
+
Effect_exports.timeout(timeout),
|
|
262
|
+
Effect_exports.catchTag(
|
|
263
|
+
"TimeoutError",
|
|
264
|
+
() => (
|
|
265
|
+
// Both the capability deadline and the native engine deadline describe
|
|
266
|
+
// the same cancellation, regardless of which timer fires first.
|
|
267
|
+
Effect_exports.fail(new WorkflowFailure({ reason: "engine", retryable: true }))
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
);
|
|
271
|
+
};
|
|
272
|
+
const runStep = (name, options, callback) => Effect_exports.gen(function* () {
|
|
273
|
+
const stepId = yield* identify("do", name);
|
|
274
|
+
const parsed = yield* Schema_exports.decodeUnknownEffect(WorkflowStepOptions)(options).pipe(
|
|
275
|
+
Effect_exports.mapError(() => new WorkflowFailure({ reason: "input", retryable: false }))
|
|
276
|
+
);
|
|
277
|
+
return yield* execution.driver.do(
|
|
278
|
+
`do:${name.slice(0, 100)}:${stepId}`,
|
|
279
|
+
parsed,
|
|
280
|
+
() => attempt(
|
|
281
|
+
parsed,
|
|
282
|
+
Effect_exports.scoped(
|
|
283
|
+
Effect_exports.gen(function* () {
|
|
284
|
+
const controller = yield* Effect_exports.acquireRelease(
|
|
285
|
+
Effect_exports.sync(() => new AbortController()),
|
|
286
|
+
(controller2) => Effect_exports.sync(() => controller2.abort())
|
|
287
|
+
);
|
|
288
|
+
const step2 = yield* fresh(stepId, AbortSignal.any([signal, controller.signal]));
|
|
289
|
+
const result = yield* workflowSafe(
|
|
290
|
+
fromPromise(callback, "step")(step2.context),
|
|
291
|
+
step2.secrets
|
|
292
|
+
);
|
|
293
|
+
return yield* Schema_exports.decodeUnknownEffect(WorkflowValue)(result).pipe(
|
|
294
|
+
Effect_exports.mapError(
|
|
295
|
+
() => new WorkflowFailure({
|
|
296
|
+
reason: "output",
|
|
297
|
+
retryable: false,
|
|
298
|
+
message: "The step returned a value that is not JSON, such as undefined, or is larger than 1 MiB. Return null for no result."
|
|
299
|
+
})
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
})
|
|
303
|
+
).pipe(Effect_exports.provideContext(services))
|
|
304
|
+
)
|
|
305
|
+
).pipe(Effect_exports.mapError(inStep(name)));
|
|
306
|
+
});
|
|
307
|
+
const operationStep = (kind, name, operation, input, options) => Effect_exports.gen(function* () {
|
|
308
|
+
const target = nativeOperation(operation);
|
|
309
|
+
const root = definition.tools?.kind === "router" ? definition.tools : void 0;
|
|
310
|
+
const match = declaredOperations(root).find(
|
|
311
|
+
(entry) => entry.operation === target && entry.operation.kind === kind
|
|
312
|
+
);
|
|
313
|
+
if (target === void 0 || match === void 0)
|
|
314
|
+
return yield* new WorkflowFailure({ reason: "operation", retryable: false });
|
|
315
|
+
const args = yield* Schema_exports.decodeUnknownEffect(JsonValue)(input).pipe(
|
|
316
|
+
Effect_exports.mapError(() => new WorkflowFailure({ reason: "input", retryable: false }))
|
|
317
|
+
);
|
|
318
|
+
const parsed = yield* Schema_exports.decodeUnknownEffect(WorkflowStepOptions)(options).pipe(
|
|
319
|
+
Effect_exports.mapError(() => new WorkflowFailure({ reason: "input", retryable: false }))
|
|
320
|
+
);
|
|
321
|
+
const stepId = yield* identify(kind, name);
|
|
322
|
+
const timeout = workflowDurationMillis(parsed.timeout ?? "10 minutes");
|
|
323
|
+
if (timeout === void 0)
|
|
324
|
+
return yield* new WorkflowFailure({ reason: "input", retryable: false });
|
|
325
|
+
return yield* execution.driver.do(
|
|
326
|
+
`${kind}:${name.slice(0, 100)}:${stepId}`,
|
|
327
|
+
parsed,
|
|
328
|
+
() => attempt(
|
|
329
|
+
parsed,
|
|
330
|
+
execution.invoke({ kind, name: match.name, input: args, stepId, timeout }).pipe(Effect_exports.provideContext(services))
|
|
331
|
+
)
|
|
332
|
+
).pipe(Effect_exports.mapError(inStep(name)));
|
|
333
|
+
});
|
|
334
|
+
const invoke = (effect) => toPromise(() => effect.pipe(Effect_exports.provideContext(services)), signal)();
|
|
335
|
+
const step = {
|
|
336
|
+
do: (name, options, run) => {
|
|
337
|
+
const callback = typeof options === "function" ? options : run;
|
|
338
|
+
if (callback === void 0)
|
|
339
|
+
return Promise.reject(new WorkflowFailure({ reason: "input", retryable: false }));
|
|
340
|
+
return invoke(runStep(name, typeof options === "function" ? {} : options, callback));
|
|
341
|
+
},
|
|
342
|
+
runQuery: (name, operation, input, options = {}) => invoke(operationStep("query", name, operation, input, options)),
|
|
343
|
+
runMutation: (name, operation, input, options = {}) => invoke(operationStep("mutation", name, operation, input, options)),
|
|
344
|
+
sleep: (name, duration) => invoke(
|
|
345
|
+
Schema_exports.decodeUnknownEffect(WorkflowDuration)(duration).pipe(
|
|
346
|
+
Effect_exports.mapError(() => new WorkflowFailure({ reason: "input", retryable: false })),
|
|
347
|
+
Effect_exports.flatMap(
|
|
348
|
+
(parsed) => identify("sleep", name).pipe(
|
|
349
|
+
Effect_exports.flatMap(
|
|
350
|
+
(id) => execution.driver.sleep(`sleep:${name.slice(0, 100)}:${id}`, parsed)
|
|
351
|
+
)
|
|
352
|
+
)
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
),
|
|
356
|
+
sleepUntil: (name, timestamp) => invoke(
|
|
357
|
+
identify("until", name).pipe(
|
|
358
|
+
Effect_exports.flatMap(
|
|
359
|
+
(id) => execution.driver.sleepUntil(
|
|
360
|
+
`until:${name.slice(0, 100)}:${id}`,
|
|
361
|
+
timestamp instanceof Date ? timestamp.getTime() : timestamp
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
)
|
|
365
|
+
)
|
|
366
|
+
};
|
|
367
|
+
return { runId: execution.runId, step };
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// src/implementation/input-problems.ts
|
|
371
|
+
var nativeObjectShape = (ast, limit) => objectShape(
|
|
372
|
+
ast.propertySignatures.flatMap(
|
|
373
|
+
({ name, type }) => typeof name === "string" ? [{ name, optional: SchemaAST_exports.isOptional(SchemaAST_exports.toEncoded(type)) }] : []
|
|
374
|
+
),
|
|
375
|
+
ast.indexSignatures.length > 0,
|
|
376
|
+
limit
|
|
377
|
+
);
|
|
378
|
+
var unionMembers = (ast) => SchemaAST_exports.isUnion(ast) ? ast.types.flatMap(unionMembers) : SchemaAST_exports.isUndefined(ast) || SchemaAST_exports.isVoid(ast) ? [] : [ast];
|
|
379
|
+
var fixedValues = (ast) => {
|
|
380
|
+
if (SchemaAST_exports.isLiteral(ast)) return [ast.literal];
|
|
381
|
+
if (SchemaAST_exports.isEnum(ast)) return ast.enums.map(([, value]) => value);
|
|
382
|
+
if (!SchemaAST_exports.isUnion(ast)) return void 0;
|
|
383
|
+
const members = unionMembers(ast).map(fixedValues);
|
|
384
|
+
return members.length > 0 && members.every((values) => values !== void 0) ? members.flat() : void 0;
|
|
385
|
+
};
|
|
386
|
+
var describeMember = (ast) => Match_exports.value(ast).pipe(
|
|
387
|
+
Match_exports.when(
|
|
388
|
+
SchemaAST_exports.isObjects,
|
|
389
|
+
(objects) => objects.propertySignatures.length > 0 ? nativeObjectShape(objects, maxAlternativeKeys) : "object"
|
|
390
|
+
),
|
|
391
|
+
Match_exports.when(SchemaAST_exports.isArrays, () => "array"),
|
|
392
|
+
Match_exports.when(SchemaAST_exports.isTemplateLiteral, () => "a string matching a template"),
|
|
393
|
+
Match_exports.when(SchemaAST_exports.isString, () => "string"),
|
|
394
|
+
Match_exports.when(SchemaAST_exports.isNumber, () => "number"),
|
|
395
|
+
Match_exports.when(SchemaAST_exports.isBoolean, () => "boolean"),
|
|
396
|
+
Match_exports.when(SchemaAST_exports.isNull, () => "null"),
|
|
397
|
+
Match_exports.orElse(() => "a value with other constraints")
|
|
398
|
+
);
|
|
399
|
+
var memberSelector = (members) => {
|
|
400
|
+
const [first] = members;
|
|
401
|
+
if (members.length < 2 || first === void 0 || !SchemaAST_exports.isObjects(first)) return void 0;
|
|
402
|
+
return first.propertySignatures.map(({ name }) => name).find(
|
|
403
|
+
(name) => typeof name === "string" && members.every(
|
|
404
|
+
(member) => SchemaAST_exports.isObjects(member) && member.propertySignatures.some(
|
|
405
|
+
(property) => property.name === name && fixedValues(SchemaAST_exports.toEncoded(property.type)) !== void 0
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
);
|
|
409
|
+
};
|
|
410
|
+
var unionExpectation = (ast) => {
|
|
411
|
+
const members = unionMembers(ast);
|
|
412
|
+
const fixed = members.map(fixedValues);
|
|
413
|
+
const values = fixed.flatMap((memberValues) => memberValues === void 0 ? [] : memberValues);
|
|
414
|
+
const others = members.filter((_, index) => fixed[index] === void 0);
|
|
415
|
+
if (others.length === 0) return allowedValues(values);
|
|
416
|
+
return unionShape(
|
|
417
|
+
[
|
|
418
|
+
...values.length === 0 ? [] : [allowedValues(values)],
|
|
419
|
+
...new Set(others.map(describeMember))
|
|
420
|
+
],
|
|
421
|
+
memberSelector(members)
|
|
422
|
+
);
|
|
423
|
+
};
|
|
424
|
+
var expectedUnion = (ast) => `Expected ${unionExpectation(ast)}`;
|
|
425
|
+
var expectedValue = (ast) => {
|
|
426
|
+
const encoded = SchemaAST_exports.toEncoded(ast);
|
|
427
|
+
const values = fixedValues(encoded);
|
|
428
|
+
if (values !== void 0) return allowedValues(values);
|
|
429
|
+
const members = unionMembers(encoded);
|
|
430
|
+
const [only] = members;
|
|
431
|
+
if (only === void 0) return void 0;
|
|
432
|
+
if (members.length > 1 && SchemaAST_exports.isUnion(encoded)) return unionExpectation(encoded);
|
|
433
|
+
if (SchemaAST_exports.isObjects(only) && only.propertySignatures.length > 0)
|
|
434
|
+
return nativeObjectShape(only);
|
|
435
|
+
const described = describeMember(only);
|
|
436
|
+
return described === "a value with other constraints" ? void 0 : described;
|
|
437
|
+
};
|
|
438
|
+
var leafHook = (issue) => SchemaIssue_exports.hasInput(issue) ? "Invalid value" : Match_exports.value(issue).pipe(
|
|
439
|
+
Match_exports.tag("InvalidType", ({ ast }) => {
|
|
440
|
+
const values = fixedValues(ast);
|
|
441
|
+
return values !== void 0 ? `Expected ${allowedValues(values)}` : SchemaAST_exports.isObjects(ast) && ast.propertySignatures.length > 0 ? `Expected ${nativeObjectShape(ast)}` : SchemaIssue_exports.defaultLeafHook(issue);
|
|
442
|
+
}),
|
|
443
|
+
Match_exports.orElse(SchemaIssue_exports.defaultLeafHook)
|
|
444
|
+
);
|
|
445
|
+
var checkHook = (issue) => SchemaIssue_exports.hasInput(issue) || SchemaIssue_exports.hasInput(issue.issue) ? SchemaIssue_exports.defaultCheckHook(issue) ?? "Invalid value" : SchemaIssue_exports.defaultCheckHook(issue);
|
|
446
|
+
var format = SchemaIssue_exports.makeFormatterStandardSchemaV1({ leafHook, checkHook });
|
|
447
|
+
var declaredKey = (parent, key) => parent !== void 0 && SchemaAST_exports.isObjects(parent) ? parent.propertySignatures.find(({ name }) => name === key)?.type : void 0;
|
|
448
|
+
var nativeKeys = (parent) => (key) => parent.indexSignatures.length > 0 || parent.propertySignatures.some(({ name }) => name === key);
|
|
449
|
+
var located = (issue, path, parent) => Match_exports.value(issue).pipe(
|
|
450
|
+
Match_exports.tag("Pointer", (pointer) => {
|
|
451
|
+
const declared = pointer.issue._tag === "MissingKey" && pointer.path.length === 1 ? declaredKey(parent, pointer.path[0]) : void 0;
|
|
452
|
+
return declared === void 0 || parent === void 0 || !SchemaAST_exports.isObjects(parent) ? located(pointer.issue, [...path, ...pointer.path]) : [
|
|
453
|
+
{
|
|
454
|
+
path: [...path, ...pointer.path],
|
|
455
|
+
message: missingKey(expectedValue(declared)),
|
|
456
|
+
accepts: nativeKeys(parent)
|
|
457
|
+
}
|
|
458
|
+
];
|
|
459
|
+
}),
|
|
460
|
+
Match_exports.tag(
|
|
461
|
+
"Composite",
|
|
462
|
+
({ ast, issues }) => issues.flatMap((issue2) => located(issue2, path, ast))
|
|
463
|
+
),
|
|
464
|
+
Match_exports.tag("Encoding", (encoding) => located(encoding.issue, path)),
|
|
465
|
+
Match_exports.tag(
|
|
466
|
+
"AnyOf",
|
|
467
|
+
({ ast, issues }) => issues.length === 0 ? [{ path, message: expectedUnion(ast) }] : issues.flatMap((issue2) => located(issue2, path))
|
|
468
|
+
),
|
|
469
|
+
Match_exports.orElse((issue2) => {
|
|
470
|
+
const accepts = issue2._tag === "Filter" ? importedSchemaKeys(issue2.filter) : void 0;
|
|
471
|
+
return format(issue2).issues.map((problem) => ({
|
|
472
|
+
path: [
|
|
473
|
+
...path,
|
|
474
|
+
...(problem.path ?? []).map((key) => typeof key === "object" ? key.key : key)
|
|
475
|
+
],
|
|
476
|
+
message: problem.message,
|
|
477
|
+
...accepts === void 0 ? {} : { accepts }
|
|
478
|
+
}));
|
|
479
|
+
})
|
|
480
|
+
);
|
|
481
|
+
var segment = (key) => typeof key === "number" ? `[${key}]` : typeof key === "string" && echoableKey(key) ? `.${key}` : "[key]";
|
|
482
|
+
var rendered = (path) => `input${path.map(segment).join("")}`;
|
|
483
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
484
|
+
var valueAt = (input, path) => path.reduce(
|
|
485
|
+
(value, key) => Array.isArray(value) && typeof key === "number" ? value[key] : isRecord(value) && typeof key === "string" && Object.hasOwn(value, key) ? value[key] : void 0,
|
|
486
|
+
input
|
|
487
|
+
);
|
|
488
|
+
var spelling = (key) => key.toLowerCase().replace(/[-_]/g, "");
|
|
489
|
+
var suppliedElsewhere = (input, path, accepts) => {
|
|
490
|
+
const key = path.at(-1);
|
|
491
|
+
if (typeof key !== "string" || !echoableKey(key)) return void 0;
|
|
492
|
+
const at = path.slice(0, -1);
|
|
493
|
+
const holder = valueAt(input, at);
|
|
494
|
+
if (!isRecord(holder)) return void 0;
|
|
495
|
+
const candidates = Object.keys(holder).filter((name) => echoableKey(name) && !accepts(name));
|
|
496
|
+
const respelled = candidates.find((name) => name !== key && spelling(name) === spelling(key));
|
|
497
|
+
const deeper = candidates.find((name) => {
|
|
498
|
+
const child = holder[name];
|
|
499
|
+
return isRecord(child) && Object.hasOwn(child, key);
|
|
500
|
+
});
|
|
501
|
+
const found = respelled !== void 0 ? [...at, respelled] : deeper !== void 0 ? [...at, deeper, key] : void 0;
|
|
502
|
+
return found === void 0 ? void 0 : `The input has ${String(found.at(-1))} at ${rendered(found)}; did you mean ${rendered(path)}?`;
|
|
503
|
+
};
|
|
504
|
+
var inputInvalid = (error, input) => {
|
|
505
|
+
if (!Schema_exports.isSchemaError(error)) return new HostInputInvalid();
|
|
506
|
+
const problems = located(error.issue, []).slice(0, maxInputProblems).map(({ path, message, accepts }) => {
|
|
507
|
+
const elsewhere = accepts !== void 0 && message.startsWith(missingKeyText) ? suppliedElsewhere(input, path, accepts) : void 0;
|
|
508
|
+
return `${rendered(path)}: ${message}${elsewhere === void 0 ? "" : `. ${elsewhere}`}`.slice(
|
|
509
|
+
0,
|
|
510
|
+
512
|
|
511
|
+
);
|
|
512
|
+
});
|
|
513
|
+
return new HostInputInvalid({ problems });
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// src/implementation/webhooks.ts
|
|
517
|
+
var withoutUser = ({
|
|
518
|
+
user: _user,
|
|
519
|
+
...context
|
|
520
|
+
}) => context;
|
|
521
|
+
var safe = (work, error) => Effect_exports.suspend(work).pipe(
|
|
522
|
+
Effect_exports.catchCause(
|
|
523
|
+
(cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.fail(error)
|
|
524
|
+
)
|
|
525
|
+
);
|
|
526
|
+
var sourceRequirement = (account) => "app" in account ? { scope: "app", name: account.app } : { scope: "user", name: account.user };
|
|
527
|
+
var dispatchWebhook = (definition, command, context, slots) => Effect_exports.gen(function* () {
|
|
528
|
+
const hooks = definition.webhooks ?? {};
|
|
529
|
+
if (command.operation === "webhooks") {
|
|
530
|
+
return yield* Effect_exports.forEach(
|
|
531
|
+
Object.entries(hooks),
|
|
532
|
+
([name, hook2]) => safe(
|
|
533
|
+
() => Effect_exports.gen(function* () {
|
|
534
|
+
const source2 = sourceRequirement(hook2.account);
|
|
535
|
+
if (!Object.hasOwn(slots[source2.scope], source2.name))
|
|
536
|
+
return yield* new HostDeclarationInvalid();
|
|
537
|
+
const describe = (schema) => {
|
|
538
|
+
const imported = importedJsonSchema(schema);
|
|
539
|
+
if (imported !== void 0) return Schema_exports.decodeUnknownEffect(JsonObject)(imported);
|
|
540
|
+
const document = Schema_exports.toJsonSchemaDocument(schema);
|
|
541
|
+
return Schema_exports.decodeUnknownEffect(JsonObject)({
|
|
542
|
+
...document.schema,
|
|
543
|
+
$defs: document.definitions
|
|
544
|
+
});
|
|
545
|
+
};
|
|
546
|
+
const configSchema = yield* describe(hook2.config);
|
|
547
|
+
if (hook2.setup === void 0 ? hook2.register === void 0 || hook2.unregister === void 0 : hook2.register !== void 0 || hook2.unregister !== void 0)
|
|
548
|
+
return yield* new HostDeclarationInvalid();
|
|
549
|
+
const setup = hook2.setup === void 0 ? void 0 : {
|
|
550
|
+
...yield* Schema_exports.decodeUnknownEffect(ManualWebhookSetup)(hook2.setup),
|
|
551
|
+
stateSchema: yield* describe(hook2.state)
|
|
552
|
+
};
|
|
553
|
+
return yield* Schema_exports.decodeUnknownEffect(HostedWebhook)({
|
|
554
|
+
name,
|
|
555
|
+
account: source2.name,
|
|
556
|
+
configSchema,
|
|
557
|
+
...setup === void 0 ? {} : { setup }
|
|
558
|
+
});
|
|
559
|
+
}),
|
|
560
|
+
new HostDeclarationInvalid()
|
|
561
|
+
)
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
const hook = Object.hasOwn(hooks, command.name) ? hooks[command.name] : void 0;
|
|
565
|
+
if (hook === void 0) return yield* new HostOperationNotFound();
|
|
566
|
+
const source = sourceRequirement(hook.account);
|
|
567
|
+
const scope = source.scope === "app" ? context.app : context.user;
|
|
568
|
+
if (scope === void 0) return yield* new HostAccountsInvalid();
|
|
569
|
+
const bound = Object.hasOwn(scope.accounts, source.name) ? scope.accounts[source.name] : void 0;
|
|
570
|
+
const account = (Array.isArray(bound) ? bound : [bound]).find(
|
|
571
|
+
(value) => typeof value === "object" && value !== null && "id" in value && value.id === command.sourceAccount
|
|
572
|
+
);
|
|
573
|
+
if (account === void 0) return yield* new HostInputInvalid();
|
|
574
|
+
const hookContext = source.scope === "app" ? withoutUser(context) : context;
|
|
575
|
+
const config = yield* safe(
|
|
576
|
+
() => Schema_exports.decodeUnknownEffect(hook.config)(command.config),
|
|
577
|
+
new HostInputInvalid()
|
|
578
|
+
);
|
|
579
|
+
if (command.operation === "webhook-validate")
|
|
580
|
+
return yield* safe(
|
|
581
|
+
() => Schema_exports.decodeUnknownEffect(JsonValue)(config),
|
|
582
|
+
new HostInputInvalid()
|
|
583
|
+
);
|
|
584
|
+
const input = {
|
|
585
|
+
config,
|
|
586
|
+
subscriptionId: command.subscriptionId,
|
|
587
|
+
account,
|
|
588
|
+
callbackUrl: command.callbackUrl,
|
|
589
|
+
secret: command.secret
|
|
590
|
+
};
|
|
591
|
+
const invoke = (callback, value) => safe(() => callback(hookContext, value), new HostOperationFailed());
|
|
592
|
+
if (command.operation === "webhook-complete") {
|
|
593
|
+
if (hook.setup === void 0) return yield* new HostInputInvalid();
|
|
594
|
+
return yield* safe(
|
|
595
|
+
() => Schema_exports.decodeUnknownEffect(hook.state)(command.state).pipe(
|
|
596
|
+
Effect_exports.flatMap(Schema_exports.decodeUnknownEffect(JsonValue))
|
|
597
|
+
),
|
|
598
|
+
new HostInputInvalid()
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
if (command.operation === "webhook-register") {
|
|
602
|
+
if (hook.register === void 0 || hook.setup !== void 0)
|
|
603
|
+
return yield* new HostOperationFailed();
|
|
604
|
+
const state2 = yield* invoke(hook.register, input);
|
|
605
|
+
return yield* safe(
|
|
606
|
+
() => Schema_exports.decodeUnknownEffect(hook.state)(state2).pipe(
|
|
607
|
+
Effect_exports.flatMap(Schema_exports.decodeUnknownEffect(JsonValue))
|
|
608
|
+
),
|
|
609
|
+
new HostOutputInvalid()
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
const state = command.state === null ? null : yield* safe(
|
|
613
|
+
() => Schema_exports.decodeUnknownEffect(hook.state)(command.state),
|
|
614
|
+
new HostInputInvalid()
|
|
615
|
+
);
|
|
616
|
+
if (command.operation === "webhook-unregister") {
|
|
617
|
+
if (hook.unregister === void 0 || hook.setup !== void 0)
|
|
618
|
+
return yield* new HostOperationFailed();
|
|
619
|
+
yield* invoke(hook.unregister, { ...input, state });
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
const body = yield* Effect_exports.fromResult(Base64_exports.decode(command.request.body)).pipe(
|
|
623
|
+
Effect_exports.mapError(() => new HostInputInvalid())
|
|
624
|
+
);
|
|
625
|
+
if (body.byteLength > defaultWebhookTransportLimits.maxBodyBytes)
|
|
626
|
+
return yield* new HostInputInvalid();
|
|
627
|
+
const request = yield* safe(
|
|
628
|
+
() => Effect_exports.sync(
|
|
629
|
+
() => new Request(command.request.url, {
|
|
630
|
+
method: command.request.method,
|
|
631
|
+
headers: command.request.headers,
|
|
632
|
+
...context.signal === void 0 ? {} : { signal: context.signal },
|
|
633
|
+
...command.request.method === "GET" || command.request.method === "HEAD" ? {} : { body: Uint8Array.from(body) }
|
|
634
|
+
})
|
|
635
|
+
),
|
|
636
|
+
new HostInputInvalid()
|
|
637
|
+
);
|
|
638
|
+
const response = yield* invoke(hook.handle, { ...input, state, request });
|
|
639
|
+
if (!(response instanceof Response)) return yield* new HostOutputInvalid();
|
|
640
|
+
const chunks = [];
|
|
641
|
+
let size = 0;
|
|
642
|
+
const stream = response.body;
|
|
643
|
+
if (stream !== null)
|
|
644
|
+
yield* Stream_exports.fromReadableStream({
|
|
645
|
+
evaluate: () => stream,
|
|
646
|
+
onError: () => new HostOutputInvalid()
|
|
647
|
+
}).pipe(
|
|
648
|
+
Stream_exports.runForEach(
|
|
649
|
+
(chunk) => Effect_exports.gen(function* () {
|
|
650
|
+
size += chunk.byteLength;
|
|
651
|
+
if (size > defaultWebhookTransportLimits.maxBodyBytes)
|
|
652
|
+
return yield* new HostOutputInvalid();
|
|
653
|
+
chunks.push(chunk);
|
|
654
|
+
})
|
|
655
|
+
),
|
|
656
|
+
appCode("webhook")
|
|
657
|
+
);
|
|
658
|
+
const bytes = new Uint8Array(size);
|
|
659
|
+
let offset = 0;
|
|
660
|
+
for (const chunk of chunks) {
|
|
661
|
+
bytes.set(chunk, offset);
|
|
662
|
+
offset += chunk.byteLength;
|
|
663
|
+
}
|
|
664
|
+
const headers = {};
|
|
665
|
+
response.headers.forEach((value, name) => {
|
|
666
|
+
headers[name] = value;
|
|
667
|
+
});
|
|
668
|
+
return yield* safe(
|
|
669
|
+
() => Schema_exports.decodeUnknownEffect(WebhookResponseData)({
|
|
670
|
+
status: response.status,
|
|
671
|
+
headers,
|
|
672
|
+
body: Base64_exports.encode(new Uint8Array(bytes))
|
|
673
|
+
}),
|
|
674
|
+
new HostOutputInvalid()
|
|
675
|
+
);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
// src/implementation/sql.ts
|
|
679
|
+
var receipts = `${reservedTablePrefix}step_receipts`;
|
|
680
|
+
var stepEvents = `${reservedTablePrefix}step_events`;
|
|
681
|
+
var migrationsTable = `${reservedTablePrefix}migrations`;
|
|
682
|
+
var legacyRows = `${reservedTablePrefix}legacy_rows`;
|
|
683
|
+
var materialize = (cursor) => {
|
|
684
|
+
const rows = cursor.toArray();
|
|
685
|
+
return {
|
|
686
|
+
columnNames: cursor.columnNames,
|
|
687
|
+
rowsRead: cursor.rowsRead,
|
|
688
|
+
rowsWritten: cursor.rowsWritten,
|
|
689
|
+
toArray: () => [...rows],
|
|
690
|
+
one: () => {
|
|
691
|
+
if (rows.length !== 1)
|
|
692
|
+
throw new Error(`Expected exactly one row, but the statement returned ${rows.length}.`);
|
|
693
|
+
return rows[0];
|
|
694
|
+
},
|
|
695
|
+
[Symbol.iterator]: () => rows[Symbol.iterator]()
|
|
696
|
+
};
|
|
697
|
+
};
|
|
698
|
+
var isThenable = (value) => (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|
|
699
|
+
var missingDatabase = () => {
|
|
700
|
+
throw new Error(
|
|
701
|
+
`This app has no database. Add its first migration, such as ${migrationsDirectory}/0001_create_notes.sql with a CREATE TABLE statement, and deploy again.`
|
|
702
|
+
);
|
|
703
|
+
};
|
|
704
|
+
var noDatabase = { exec: missingDatabase, transaction: missingDatabase };
|
|
705
|
+
var settings = [
|
|
706
|
+
"foreign_keys",
|
|
707
|
+
"defer_foreign_keys",
|
|
708
|
+
"ignore_check_constraints",
|
|
709
|
+
"legacy_alter_table",
|
|
710
|
+
"recursive_triggers",
|
|
711
|
+
"reverse_unordered_selects"
|
|
712
|
+
];
|
|
713
|
+
var readSettings = (storage) => new Map(
|
|
714
|
+
settings.map((name) => [name, Number(storage.sql.exec(`PRAGMA ${name}`).toArray()[0]?.[name])])
|
|
715
|
+
);
|
|
716
|
+
var changedSetting = (storage, baseline) => {
|
|
717
|
+
const current = readSettings(storage);
|
|
718
|
+
return settings.find((name) => current.get(name) !== baseline.get(name));
|
|
719
|
+
};
|
|
720
|
+
var restoreSettings = (storage, baseline) => {
|
|
721
|
+
const current = readSettings(storage);
|
|
722
|
+
for (const [name, value] of baseline)
|
|
723
|
+
if (current.get(name) !== value) storage.sql.exec(`PRAGMA ${name} = ${value}`);
|
|
724
|
+
storage.sql.exec("PRAGMA case_sensitive_like = OFF");
|
|
725
|
+
};
|
|
726
|
+
var ReadRolledBack = class extends Error {
|
|
727
|
+
};
|
|
728
|
+
var authorSql = (storage, options) => {
|
|
729
|
+
let closed = false;
|
|
730
|
+
const live = () => {
|
|
731
|
+
if (closed || options.signal.aborted)
|
|
732
|
+
throw new Error("This call has ended, so its ctx.sql no longer runs statements.");
|
|
733
|
+
};
|
|
734
|
+
let open = false;
|
|
735
|
+
let transactions = 0;
|
|
736
|
+
const baseline = readSettings(storage);
|
|
737
|
+
const checkSettings = () => {
|
|
738
|
+
const changed = changedSetting(storage, baseline);
|
|
739
|
+
if (changed !== void 0)
|
|
740
|
+
throw refuse(
|
|
741
|
+
new Error(
|
|
742
|
+
`SQLite connection settings belong to Executor, so app code cannot change PRAGMA ${changed}; the statement was rolled back. Migrations may change settings while they run.`
|
|
743
|
+
)
|
|
744
|
+
);
|
|
745
|
+
};
|
|
746
|
+
const changes = () => Number(storage.sql.exec("SELECT total_changes() AS n").toArray()[0]?.n);
|
|
747
|
+
let violation;
|
|
748
|
+
const refuse = (error) => {
|
|
749
|
+
if (open) violation ??= error;
|
|
750
|
+
return error;
|
|
751
|
+
};
|
|
752
|
+
const run = (query, bindings, mode) => {
|
|
753
|
+
try {
|
|
754
|
+
return runStatement(query, bindings, mode);
|
|
755
|
+
} finally {
|
|
756
|
+
restoreSettings(storage, baseline);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
const runStatement = (query, bindings, mode) => {
|
|
760
|
+
live();
|
|
761
|
+
if (bindings.length > sqlBindingsLimit)
|
|
762
|
+
throw refuse(
|
|
763
|
+
new Error(
|
|
764
|
+
`A statement can bind at most ${sqlBindingsLimit} values, but this one binds ${bindings.length}. Pass a long list as one JSON array and read it with json_each(?), or split the work into batches.`
|
|
765
|
+
)
|
|
766
|
+
);
|
|
767
|
+
if (mode === "write") {
|
|
768
|
+
const statement = () => {
|
|
769
|
+
const cursor = materialize(storage.sql.exec(query, ...bindings));
|
|
770
|
+
checkSettings();
|
|
771
|
+
return cursor;
|
|
772
|
+
};
|
|
773
|
+
if (!open) return storage.transactionSync(statement);
|
|
774
|
+
try {
|
|
775
|
+
return statement();
|
|
776
|
+
} catch (error) {
|
|
777
|
+
refuse(error instanceof Error ? error : new Error(String(error)));
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (open) {
|
|
782
|
+
return runStatement(query, bindings, "write");
|
|
783
|
+
}
|
|
784
|
+
const before = changes();
|
|
785
|
+
let read;
|
|
786
|
+
let changed = false;
|
|
787
|
+
try {
|
|
788
|
+
storage.transactionSync(() => {
|
|
789
|
+
read = materialize(storage.sql.exec(query, ...bindings));
|
|
790
|
+
changed = changes() !== before;
|
|
791
|
+
checkSettings();
|
|
792
|
+
throw new ReadRolledBack();
|
|
793
|
+
});
|
|
794
|
+
} catch (error) {
|
|
795
|
+
if (!(error instanceof ReadRolledBack)) throw error;
|
|
796
|
+
}
|
|
797
|
+
if (changed || read === void 0)
|
|
798
|
+
throw new Error(
|
|
799
|
+
options.step !== void 0 ? "A workflow step mutation writes only inside ctx.sql.transaction(...), so a retried step applies its writes once." : "Queries are read-only. Change data from a mutation."
|
|
800
|
+
);
|
|
801
|
+
return read;
|
|
802
|
+
};
|
|
803
|
+
const reader = {
|
|
804
|
+
exec: (query, ...bindings) => run(query, bindings, "read")
|
|
805
|
+
};
|
|
806
|
+
const transactionHandle = () => {
|
|
807
|
+
let active = true;
|
|
808
|
+
const tx = {
|
|
809
|
+
exec: (query, ...bindings) => {
|
|
810
|
+
if (!active)
|
|
811
|
+
throw new Error("This transaction has ended, so its tx no longer runs statements.");
|
|
812
|
+
return run(query, bindings, "write");
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
return {
|
|
816
|
+
tx,
|
|
817
|
+
end: () => {
|
|
818
|
+
active = false;
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
};
|
|
822
|
+
const transaction = (work) => {
|
|
823
|
+
live();
|
|
824
|
+
if (open) throw new Error("ctx.sql.transaction(...) cannot be nested.");
|
|
825
|
+
const step = options.step;
|
|
826
|
+
if (step !== void 0 && transactions > 0)
|
|
827
|
+
throw new Error(
|
|
828
|
+
"A workflow step mutation opens at most one ctx.sql.transaction(...), which records the step's result."
|
|
829
|
+
);
|
|
830
|
+
transactions += 1;
|
|
831
|
+
violation = void 0;
|
|
832
|
+
const handle = transactionHandle();
|
|
833
|
+
const emittedBefore = options.emitted?.length;
|
|
834
|
+
const discardEmitted = () => {
|
|
835
|
+
if (options.emitted !== void 0 && emittedBefore !== void 0)
|
|
836
|
+
options.emitted.length = emittedBefore;
|
|
837
|
+
};
|
|
838
|
+
const body = () => {
|
|
839
|
+
open = true;
|
|
840
|
+
try {
|
|
841
|
+
const complete = () => {
|
|
842
|
+
const value = work(handle.tx);
|
|
843
|
+
if (isThenable(value))
|
|
844
|
+
throw new Error(
|
|
845
|
+
"ctx.sql.transaction(...) callbacks are synchronous and cannot await. Call outside services before or after the transaction."
|
|
846
|
+
);
|
|
847
|
+
if (violation !== void 0) throw violation;
|
|
848
|
+
return value;
|
|
849
|
+
};
|
|
850
|
+
if (step === void 0) return complete();
|
|
851
|
+
storage.sql.exec(
|
|
852
|
+
`CREATE TABLE IF NOT EXISTS ${receipts} (id TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, result TEXT NOT NULL) WITHOUT ROWID`
|
|
853
|
+
);
|
|
854
|
+
const saved = storage.sql.exec(`SELECT fingerprint, result FROM ${receipts} WHERE id = ?`, step.key).toArray()[0];
|
|
855
|
+
storage.sql.exec(
|
|
856
|
+
`CREATE TABLE IF NOT EXISTS ${stepEvents} (id TEXT PRIMARY KEY, events TEXT NOT NULL) WITHOUT ROWID`
|
|
857
|
+
);
|
|
858
|
+
if (saved !== void 0) {
|
|
859
|
+
if (saved.fingerprint !== step.fingerprint)
|
|
860
|
+
throw new Error("This workflow step already ran with different input.");
|
|
861
|
+
const replayed = storage.sql.exec(`SELECT events FROM ${stepEvents} WHERE id = ?`, step.key).toArray()[0];
|
|
862
|
+
if (replayed !== void 0 && options.emitted !== void 0)
|
|
863
|
+
options.emitted.push(...JSON.parse(String(replayed.events)));
|
|
864
|
+
return JSON.parse(String(saved.result));
|
|
865
|
+
}
|
|
866
|
+
const encoded = JSON.stringify(complete() ?? null);
|
|
867
|
+
const emitted = options.emitted?.slice(emittedBefore ?? 0) ?? [];
|
|
868
|
+
if (emitted.length > 0)
|
|
869
|
+
storage.sql.exec(
|
|
870
|
+
`INSERT INTO ${stepEvents} (id, events) VALUES (?, ?)`,
|
|
871
|
+
step.key,
|
|
872
|
+
JSON.stringify(emitted)
|
|
873
|
+
);
|
|
874
|
+
storage.sql.exec(
|
|
875
|
+
`INSERT INTO ${receipts} (id, fingerprint, result) VALUES (?, ?, ?)`,
|
|
876
|
+
step.key,
|
|
877
|
+
step.fingerprint,
|
|
878
|
+
encoded
|
|
879
|
+
);
|
|
880
|
+
return JSON.parse(encoded);
|
|
881
|
+
} finally {
|
|
882
|
+
open = false;
|
|
883
|
+
handle.end();
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
try {
|
|
887
|
+
return storage.transactionSync(body);
|
|
888
|
+
} catch (error) {
|
|
889
|
+
discardEmitted();
|
|
890
|
+
throw error;
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
const writer = {
|
|
894
|
+
// A step's writes go through its one transaction, which also records its receipt.
|
|
895
|
+
exec: (query, ...bindings) => run(query, bindings, options.step ? "read" : "write"),
|
|
896
|
+
transaction
|
|
897
|
+
};
|
|
898
|
+
return {
|
|
899
|
+
reader,
|
|
900
|
+
writer,
|
|
901
|
+
close: () => {
|
|
902
|
+
closed = true;
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
};
|
|
906
|
+
var MigrationFileInvalid = class extends Schema_exports.TaggedError()(
|
|
907
|
+
"MigrationFileInvalid",
|
|
908
|
+
{ path: Schema_exports.String, reason: Schema_exports.String }
|
|
909
|
+
) {
|
|
910
|
+
get message() {
|
|
911
|
+
return `${this.path}: ${this.reason}`;
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
var MigrationHistoryChanged = class extends Schema_exports.TaggedError()(
|
|
915
|
+
"MigrationHistoryChanged",
|
|
916
|
+
{ reason: Schema_exports.String }
|
|
917
|
+
) {
|
|
918
|
+
get message() {
|
|
919
|
+
return `${this.reason} Applied migrations cannot change: restore the original file and add a new migration instead. Nothing was applied; the previous deployment is still active.`;
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
var MigrationFailed = class extends Schema_exports.TaggedError()("MigrationFailed", {
|
|
923
|
+
path: Schema_exports.String,
|
|
924
|
+
reason: Schema_exports.String
|
|
925
|
+
}) {
|
|
926
|
+
get message() {
|
|
927
|
+
return `${this.path} failed: ${this.reason}. No migrations were applied; the previous deployment is still active.`;
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
var migrationName = /^(\d+)_[A-Za-z0-9_-]+\.sql$/;
|
|
931
|
+
var sha256 = (text) => (
|
|
932
|
+
// oxlint-disable-next-line executor/authored-code-through-adapter -- Web Crypto
|
|
933
|
+
Effect_exports.promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))).pipe(
|
|
934
|
+
Effect_exports.map(
|
|
935
|
+
(digest) => Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")
|
|
936
|
+
)
|
|
937
|
+
)
|
|
938
|
+
);
|
|
939
|
+
var statementsOf = (script) => {
|
|
940
|
+
let end = 0;
|
|
941
|
+
let index = 0;
|
|
942
|
+
while (index < script.length) {
|
|
943
|
+
const char = script[index];
|
|
944
|
+
const next = script[index + 1];
|
|
945
|
+
if (char === "-" && next === "-") {
|
|
946
|
+
const newline = script.indexOf("\n", index);
|
|
947
|
+
index = newline === -1 ? script.length : newline + 1;
|
|
948
|
+
continue;
|
|
949
|
+
}
|
|
950
|
+
if (char === "/" && next === "*") {
|
|
951
|
+
const close = script.indexOf("*/", index + 2);
|
|
952
|
+
index = close === -1 ? script.length : close + 2;
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
const closing = char === "'" || char === '"' || char === "`" ? char : char === "[" ? "]" : void 0;
|
|
956
|
+
if (closing !== void 0) {
|
|
957
|
+
let after = index + 1;
|
|
958
|
+
while (true) {
|
|
959
|
+
const found = script.indexOf(closing, after);
|
|
960
|
+
if (found === -1) {
|
|
961
|
+
after = script.length;
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
if (closing !== "]" && script[found + 1] === closing) {
|
|
965
|
+
after = found + 2;
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
after = found + 1;
|
|
969
|
+
break;
|
|
970
|
+
}
|
|
971
|
+
index = after;
|
|
972
|
+
end = index;
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
index += 1;
|
|
976
|
+
if (char !== void 0 && !/\s/u.test(char)) end = index;
|
|
977
|
+
}
|
|
978
|
+
return script.slice(0, end);
|
|
979
|
+
};
|
|
980
|
+
var hasMigrations = (files) => files.some(
|
|
981
|
+
(file) => file.path.startsWith(`${migrationsDirectory}/`) && file.path.endsWith(".sql")
|
|
982
|
+
);
|
|
983
|
+
var migrationsOf = (files) => Effect_exports.gen(function* () {
|
|
984
|
+
const found = [];
|
|
985
|
+
for (const file of files) {
|
|
986
|
+
if (!file.path.startsWith(`${migrationsDirectory}/`) || !file.path.endsWith(".sql")) continue;
|
|
987
|
+
const name = file.path.slice(migrationsDirectory.length + 1);
|
|
988
|
+
const match = migrationName.exec(name);
|
|
989
|
+
if (name.includes("/") || match === null)
|
|
990
|
+
return yield* new MigrationFileInvalid({
|
|
991
|
+
path: file.path,
|
|
992
|
+
reason: "migrations are files directly in migrations/ named like 0001_create_notes.sql: a number that orders them, an underscore, then a name."
|
|
993
|
+
});
|
|
994
|
+
const content = file.content.replaceAll("\r\n", "\n");
|
|
995
|
+
found.push({
|
|
996
|
+
path: file.path,
|
|
997
|
+
order: Number(match[1]),
|
|
998
|
+
content,
|
|
999
|
+
hash: yield* sha256(content)
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
const ordered = [...found].sort((left, right) => left.order - right.order);
|
|
1003
|
+
for (const [index, migration] of ordered.entries()) {
|
|
1004
|
+
const previous = ordered[index - 1];
|
|
1005
|
+
if (previous !== void 0 && previous.order === migration.order)
|
|
1006
|
+
return yield* new MigrationFileInvalid({
|
|
1007
|
+
path: migration.path,
|
|
1008
|
+
reason: `it has the same number as ${previous.path}; give each migration its own number.`
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
return ordered;
|
|
1012
|
+
});
|
|
1013
|
+
var History = Schema_exports.Array(
|
|
1014
|
+
Schema_exports.Struct({ id: Schema_exports.Number, path: Schema_exports.String, hash: Schema_exports.String })
|
|
1015
|
+
);
|
|
1016
|
+
var migrate = (storage, files) => Effect_exports.gen(function* () {
|
|
1017
|
+
const migrations = yield* migrationsOf(files);
|
|
1018
|
+
const baseline = readSettings(storage);
|
|
1019
|
+
return yield* Effect_exports.sync(
|
|
1020
|
+
() => storage.transactionSync(() => {
|
|
1021
|
+
storage.sql.exec(
|
|
1022
|
+
`CREATE TABLE IF NOT EXISTS ${migrationsTable} (id INTEGER PRIMARY KEY, path TEXT NOT NULL, hash TEXT NOT NULL, applied_at TEXT NOT NULL)`
|
|
1023
|
+
);
|
|
1024
|
+
const stored = storage.sql.exec("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'app_rows'").toArray().length > 0;
|
|
1025
|
+
storage.sql.exec(`DROP VIEW IF EXISTS ${legacyRows}`);
|
|
1026
|
+
storage.sql.exec(
|
|
1027
|
+
`CREATE VIEW ${legacyRows} (table_name, id, body) AS ${stored ? "SELECT table_name, id, body FROM app_rows" : "SELECT '', '', '' WHERE 0"}`
|
|
1028
|
+
);
|
|
1029
|
+
const history = Schema_exports.decodeUnknownSync(History)(
|
|
1030
|
+
storage.sql.exec(`SELECT id, path, hash FROM ${migrationsTable} ORDER BY id`).toArray()
|
|
1031
|
+
);
|
|
1032
|
+
for (const [index, applied] of history.entries()) {
|
|
1033
|
+
const current = migrations[index];
|
|
1034
|
+
if (current === void 0)
|
|
1035
|
+
throw new MigrationHistoryChanged({
|
|
1036
|
+
reason: `${applied.path} was applied to this app's database but is no longer in migrations/.`
|
|
1037
|
+
});
|
|
1038
|
+
if (current.path !== applied.path)
|
|
1039
|
+
throw new MigrationHistoryChanged({
|
|
1040
|
+
reason: `Migration ${index + 1} was applied as ${applied.path}, but the build has ${current.path} in its place.`
|
|
1041
|
+
});
|
|
1042
|
+
if (current.hash !== applied.hash)
|
|
1043
|
+
throw new MigrationHistoryChanged({
|
|
1044
|
+
reason: `${applied.path} changed after it was applied to this app's database.`
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
const pending = migrations.slice(history.length);
|
|
1048
|
+
const appliedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1049
|
+
for (const [offset, migration] of pending.entries()) {
|
|
1050
|
+
const statements = statementsOf(migration.content);
|
|
1051
|
+
try {
|
|
1052
|
+
if (statements !== "") storage.sql.exec(statements);
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
throw new MigrationFailed({
|
|
1055
|
+
path: migration.path,
|
|
1056
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
storage.sql.exec(
|
|
1060
|
+
`INSERT INTO ${migrationsTable} (id, path, hash, applied_at) VALUES (?, ?, ?, ?)`,
|
|
1061
|
+
history.length + offset + 1,
|
|
1062
|
+
migration.path,
|
|
1063
|
+
migration.hash,
|
|
1064
|
+
appliedAt
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
return pending.map((migration, offset) => ({
|
|
1068
|
+
id: history.length + offset + 1,
|
|
1069
|
+
name: migration.path
|
|
1070
|
+
}));
|
|
1071
|
+
})
|
|
1072
|
+
).pipe(
|
|
1073
|
+
// Both are thrown inside the transaction so that it rolls back; they are expected failures.
|
|
1074
|
+
Effect_exports.catchDefect(
|
|
1075
|
+
(defect) => Schema_exports.is(MigrationFailed)(defect) || Schema_exports.is(MigrationHistoryChanged)(defect) ? Effect_exports.fail(defect) : Effect_exports.die(defect)
|
|
1076
|
+
),
|
|
1077
|
+
Effect_exports.ensuring(Effect_exports.sync(() => restoreSettings(storage, baseline)))
|
|
1078
|
+
);
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
// src/implementation/host.ts
|
|
1082
|
+
var WireCatalog = Schema_exports.Struct({
|
|
1083
|
+
tools: Schema_exports.Array(Schema_exports.Union([HostedTool, HostedToolSummary])),
|
|
1084
|
+
routers: Schema_exports.Array(HostedRouter)
|
|
1085
|
+
});
|
|
1086
|
+
function safe2(work, failure) {
|
|
1087
|
+
return Effect_exports.suspend(work).pipe(
|
|
1088
|
+
Effect_exports.catchCause(
|
|
1089
|
+
(cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.fail(failure)
|
|
1090
|
+
)
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
var evaluationSafe = (work, secrets) => work.pipe(
|
|
1094
|
+
Effect_exports.catchCause((cause) => {
|
|
1095
|
+
if (Cause_exports.hasInterrupts(cause)) return Effect_exports.interrupt;
|
|
1096
|
+
const error = Cause_exports.squash(cause);
|
|
1097
|
+
const provider = parseProviderError(error);
|
|
1098
|
+
const skills = parseSkillLoadFailed(error);
|
|
1099
|
+
const mcp = parseMcpError(error, secrets);
|
|
1100
|
+
return Effect_exports.fail(
|
|
1101
|
+
Option_exports.isSome(provider) ? leavingProviderError(provider.value, secrets, "discover") : Option_exports.isSome(skills) ? skills.value : Option_exports.isSome(mcp) ? mcp.value : new HostEvaluationFailed(failureDetail(error, secrets))
|
|
1102
|
+
);
|
|
1103
|
+
})
|
|
1104
|
+
);
|
|
1105
|
+
var declarationInvalid = (summary, cause, secrets = []) => new HostDeclarationInvalid({
|
|
1106
|
+
source: "app",
|
|
1107
|
+
errorName: "HostDeclarationInvalid",
|
|
1108
|
+
message: boundFailureMessage(
|
|
1109
|
+
cause === void 0 ? summary : `${summary}: ${describeFailure(cause)}`,
|
|
1110
|
+
secrets
|
|
1111
|
+
)
|
|
1112
|
+
});
|
|
1113
|
+
var declarationSafe = (work, summary) => Effect_exports.suspend(work).pipe(
|
|
1114
|
+
Effect_exports.catchCause(
|
|
1115
|
+
(cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.fail(declarationInvalid(summary, Cause_exports.squash(cause)))
|
|
1116
|
+
)
|
|
1117
|
+
);
|
|
1118
|
+
function declaredExposure(exposure) {
|
|
1119
|
+
const named = (kind) => Object.entries(exposure ?? {}).filter(([, value]) => value === kind).map(([field]) => field).sort();
|
|
1120
|
+
const plain = named("plain");
|
|
1121
|
+
const raw = named("raw");
|
|
1122
|
+
return { ...plain.length === 0 ? {} : { plain }, ...raw.length === 0 ? {} : { raw } };
|
|
1123
|
+
}
|
|
1124
|
+
function declaredPlacements(request) {
|
|
1125
|
+
return request === void 0 || request.length === 0 ? {} : { request: normalizePlacements(request) };
|
|
1126
|
+
}
|
|
1127
|
+
function providerDeclaration(provider) {
|
|
1128
|
+
return declarationSafe(
|
|
1129
|
+
() => Effect_exports.gen(function* () {
|
|
1130
|
+
const auth = /* @__PURE__ */ new Map();
|
|
1131
|
+
for (const [name, method] of Object.entries(provider.auth)) {
|
|
1132
|
+
switch (method._tag) {
|
|
1133
|
+
case "secrets":
|
|
1134
|
+
auth.set(name, {
|
|
1135
|
+
type: "secrets",
|
|
1136
|
+
label: method.label,
|
|
1137
|
+
fields: yield* jsonSchemaDocument(method.fields),
|
|
1138
|
+
...declaredExposure(method.exposure),
|
|
1139
|
+
...declaredPlacements(method.request)
|
|
1140
|
+
});
|
|
1141
|
+
break;
|
|
1142
|
+
case "oauth2":
|
|
1143
|
+
auth.set(name, {
|
|
1144
|
+
type: "oauth2",
|
|
1145
|
+
...method.config,
|
|
1146
|
+
response: yield* jsonSchemaDocument(method.response),
|
|
1147
|
+
...declaredExposure(method.exposure),
|
|
1148
|
+
...declaredPlacements(method.request)
|
|
1149
|
+
});
|
|
1150
|
+
break;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return yield* Schema_exports.decodeUnknownEffect(DeclaredProvider)({
|
|
1154
|
+
name: provider.name,
|
|
1155
|
+
auth: Object.fromEntries(auth),
|
|
1156
|
+
// Order and repetition carry no meaning, so neither changes the provider's identity.
|
|
1157
|
+
...provider.hosts === void 0 ? {} : { hosts: [...new Set(provider.hosts)].sort() }
|
|
1158
|
+
});
|
|
1159
|
+
}),
|
|
1160
|
+
`Account provider "${provider.name}" has an invalid declaration`
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
function declaredSlots(slots) {
|
|
1164
|
+
return Effect_exports.gen(function* () {
|
|
1165
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
1166
|
+
for (const [slot, selection] of Object.entries(slots)) {
|
|
1167
|
+
const provider = selection instanceof ManyAccounts ? selection.provider : selection;
|
|
1168
|
+
accounts.set(slot, {
|
|
1169
|
+
cardinality: selection instanceof ManyAccounts ? "many" : "one",
|
|
1170
|
+
definition: yield* providerDeclaration(provider),
|
|
1171
|
+
...provider.health === void 0 ? {} : { health: true }
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
return Object.fromEntries(accounts);
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
function requirements(slots, sql, events, icons) {
|
|
1178
|
+
return Effect_exports.gen(function* () {
|
|
1179
|
+
return yield* Schema_exports.decodeUnknownEffect(DeclaredRequirements)({
|
|
1180
|
+
accounts: { app: yield* declaredSlots(slots.app), user: yield* declaredSlots(slots.user) },
|
|
1181
|
+
capabilities: { skills: true, toolIndex: true, skillSources: true, scheduledTools: true },
|
|
1182
|
+
...sql ? { sql: true } : {},
|
|
1183
|
+
...events === void 0 || Object.keys(events).length === 0 ? {} : {
|
|
1184
|
+
events: yield* declarationSafe(
|
|
1185
|
+
() => declaredEvents(events),
|
|
1186
|
+
"The app's event declarations are invalid"
|
|
1187
|
+
)
|
|
1188
|
+
},
|
|
1189
|
+
...icons === void 0 ? {} : { icons }
|
|
1190
|
+
}).pipe(
|
|
1191
|
+
Effect_exports.mapError(
|
|
1192
|
+
(cause) => declarationInvalid("The app's account declarations are invalid", cause)
|
|
1193
|
+
)
|
|
1194
|
+
);
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
function canonical(value) {
|
|
1198
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
1199
|
+
if (value !== null && typeof value === "object") {
|
|
1200
|
+
return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(",")}}`;
|
|
1201
|
+
}
|
|
1202
|
+
return JSON.stringify(value);
|
|
1203
|
+
}
|
|
1204
|
+
function withoutGrants({ hosts: _hosts, ...provider }) {
|
|
1205
|
+
return {
|
|
1206
|
+
...provider,
|
|
1207
|
+
auth: Object.fromEntries(
|
|
1208
|
+
Object.entries(provider.auth).map(([name, { request: _request, ...method }]) => [
|
|
1209
|
+
name,
|
|
1210
|
+
method
|
|
1211
|
+
])
|
|
1212
|
+
)
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
function bindScope(slots, declarations, selected) {
|
|
1216
|
+
return Effect_exports.gen(function* () {
|
|
1217
|
+
if (Object.keys(selected).some((slot) => !Object.hasOwn(slots, slot))) {
|
|
1218
|
+
return yield* Effect_exports.fail(new HostAccountsInvalid());
|
|
1219
|
+
}
|
|
1220
|
+
const bound = /* @__PURE__ */ new Map();
|
|
1221
|
+
for (const [slot, selection] of Object.entries(slots)) {
|
|
1222
|
+
const requirement = Object.hasOwn(declarations, slot) ? declarations[slot] : void 0;
|
|
1223
|
+
const supplied = Object.hasOwn(selected, slot) ? selected[slot] : void 0;
|
|
1224
|
+
if (requirement === void 0 || supplied === void 0)
|
|
1225
|
+
return yield* Effect_exports.fail(new HostAccountsInvalid());
|
|
1226
|
+
const provider = selection instanceof ManyAccounts ? selection.provider : selection;
|
|
1227
|
+
const many = selection instanceof ManyAccounts;
|
|
1228
|
+
if (many !== Array.isArray(supplied)) return yield* Effect_exports.fail(new HostAccountsInvalid());
|
|
1229
|
+
const accounts = Array.isArray(supplied) ? supplied : [supplied];
|
|
1230
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1231
|
+
const values = [];
|
|
1232
|
+
for (const account of accounts) {
|
|
1233
|
+
if (ids.has(account.id)) return yield* Effect_exports.fail(new HostAccountsInvalid());
|
|
1234
|
+
ids.add(account.id);
|
|
1235
|
+
if (canonical(withoutGrants(account.provider)) !== canonical(withoutGrants(requirement.definition)) || !Object.hasOwn(provider.auth, account.method))
|
|
1236
|
+
return yield* Effect_exports.fail(new HostAccountsInvalid());
|
|
1237
|
+
const method = provider.auth[account.method];
|
|
1238
|
+
if (method === void 0) return yield* Effect_exports.fail(new HostAccountsInvalid());
|
|
1239
|
+
const fields = yield* Schema_exports.decodeUnknownEffect(
|
|
1240
|
+
method._tag === "secrets" ? method.fields : method.response
|
|
1241
|
+
)(account.fields);
|
|
1242
|
+
const granted = Object.hasOwn(account.provider.auth, account.method) ? account.provider.auth[account.method]?.request : void 0;
|
|
1243
|
+
values.push(withPlacements({ id: account.id, method: account.method, fields }, granted));
|
|
1244
|
+
}
|
|
1245
|
+
if (many) bound.set(slot, values);
|
|
1246
|
+
else {
|
|
1247
|
+
const account = values[0];
|
|
1248
|
+
if (account === void 0) return yield* Effect_exports.fail(new HostAccountsInvalid());
|
|
1249
|
+
bound.set(slot, account);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return Object.fromEntries(bound);
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
function bindAccounts(slots, declarations, context) {
|
|
1256
|
+
return safe2(
|
|
1257
|
+
() => Effect_exports.gen(function* () {
|
|
1258
|
+
const selected = yield* Schema_exports.decodeUnknownEffect(ResolvedAccounts)(
|
|
1259
|
+
Redacted_exports.value(context.accounts)
|
|
1260
|
+
);
|
|
1261
|
+
const app = yield* bindScope(slots.app, declarations.accounts.app, selected.app);
|
|
1262
|
+
if (selected.user === void 0) return { app: { accounts: app } };
|
|
1263
|
+
const user = yield* bindScope(slots.user, declarations.accounts.user, selected.user);
|
|
1264
|
+
return { app: { accounts: app }, user: { accounts: user } };
|
|
1265
|
+
}),
|
|
1266
|
+
new HostAccountsInvalid()
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
var userAccountIds = (bound) => Object.values(bound.user?.accounts ?? {}).flatMap(
|
|
1270
|
+
(selected) => (Array.isArray(selected) ? selected : [selected]).map(
|
|
1271
|
+
(account) => account.id
|
|
1272
|
+
)
|
|
1273
|
+
);
|
|
1274
|
+
var fetchLifetime = (signal, background) => Effect_exports.acquireRelease(
|
|
1275
|
+
Effect_exports.sync(() => {
|
|
1276
|
+
const controller = new AbortController();
|
|
1277
|
+
let refreshes = 0;
|
|
1278
|
+
let ended = false;
|
|
1279
|
+
const settle = () => {
|
|
1280
|
+
if (ended && refreshes === 0) controller.abort();
|
|
1281
|
+
};
|
|
1282
|
+
const end = () => {
|
|
1283
|
+
ended = true;
|
|
1284
|
+
settle();
|
|
1285
|
+
};
|
|
1286
|
+
signal.addEventListener("abort", end, { once: true });
|
|
1287
|
+
if (signal.aborted) end();
|
|
1288
|
+
const settled = Effect_exports.sync(() => {
|
|
1289
|
+
refreshes -= 1;
|
|
1290
|
+
settle();
|
|
1291
|
+
});
|
|
1292
|
+
return {
|
|
1293
|
+
signal: controller.signal,
|
|
1294
|
+
close: () => {
|
|
1295
|
+
signal.removeEventListener("abort", end);
|
|
1296
|
+
end();
|
|
1297
|
+
},
|
|
1298
|
+
/** Counted from the hand-off, so a refresh that has not started yet keeps the fetch. */
|
|
1299
|
+
background: (task) => Effect_exports.suspend(() => {
|
|
1300
|
+
refreshes += 1;
|
|
1301
|
+
return background(task.pipe(Effect_exports.ensuring(settled))).pipe(
|
|
1302
|
+
Effect_exports.onError(() => settled)
|
|
1303
|
+
);
|
|
1304
|
+
})
|
|
1305
|
+
};
|
|
1306
|
+
}),
|
|
1307
|
+
(lifetime) => Effect_exports.sync(lifetime.close)
|
|
1308
|
+
);
|
|
1309
|
+
function dispatch(app, request, context, signal, emitted) {
|
|
1310
|
+
return Effect_exports.scoped(
|
|
1311
|
+
Effect_exports.gen(function* () {
|
|
1312
|
+
if (!isApp(app))
|
|
1313
|
+
return yield* Effect_exports.fail(
|
|
1314
|
+
declarationInvalid("The app module's default export is not an app created by defineApp")
|
|
1315
|
+
);
|
|
1316
|
+
const native = toEffectApp(app);
|
|
1317
|
+
const secrets = accountSecrets(context.accounts);
|
|
1318
|
+
const ownsSql = hasMigrations(context.files ?? []);
|
|
1319
|
+
const declared = yield* requirements(native.accounts, ownsSql, native.events, native.icons);
|
|
1320
|
+
if (request.operation === "requirements")
|
|
1321
|
+
return yield* safe2(
|
|
1322
|
+
() => Schema_exports.decodeUnknownEffect(JsonValue)(declared),
|
|
1323
|
+
new HostDeclarationInvalid()
|
|
1324
|
+
);
|
|
1325
|
+
if (request.operation === "migrate") {
|
|
1326
|
+
if (!ownsSql) return yield* new HostOperationNotFound();
|
|
1327
|
+
const storage2 = context.storage;
|
|
1328
|
+
if (storage2 === void 0)
|
|
1329
|
+
return yield* new HostOperationFailed(
|
|
1330
|
+
failureDetail(new Error("This host gave the app no SQL storage."), secrets)
|
|
1331
|
+
);
|
|
1332
|
+
const sources = yield* Schema_exports.decodeUnknownEffect(Schema_exports.Array(SkillFile))(
|
|
1333
|
+
context.files ?? []
|
|
1334
|
+
).pipe(Effect_exports.mapError(() => new HostDeclarationInvalid()));
|
|
1335
|
+
const applied = yield* migrate(storage2, sources).pipe(
|
|
1336
|
+
Effect_exports.mapError((error) => new HostOperationFailed(failureDetail(error, secrets))),
|
|
1337
|
+
Effect_exports.withSpan("app.sql.migrate")
|
|
1338
|
+
);
|
|
1339
|
+
return yield* safe2(
|
|
1340
|
+
() => Schema_exports.decodeUnknownEffect(JsonValue)(applied),
|
|
1341
|
+
new HostOutputInvalid()
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
const lifetime = yield* Effect_exports.acquireRelease(
|
|
1345
|
+
Effect_exports.sync(() => new AbortController()),
|
|
1346
|
+
(controller) => Effect_exports.sync(() => controller.abort())
|
|
1347
|
+
);
|
|
1348
|
+
const invocationSignal = AbortSignal.any([signal, lifetime.signal]);
|
|
1349
|
+
const storage = context.storage;
|
|
1350
|
+
const sqlSession = (step) => !ownsSql ? Effect_exports.succeed({ reader: noDatabase, writer: noDatabase }) : storage === void 0 ? Effect_exports.fail(
|
|
1351
|
+
new HostOperationFailed(
|
|
1352
|
+
failureDetail(new Error("This host gave the app no SQL storage."), secrets)
|
|
1353
|
+
)
|
|
1354
|
+
) : Effect_exports.acquireRelease(
|
|
1355
|
+
Effect_exports.sync(
|
|
1356
|
+
() => authorSql(storage, {
|
|
1357
|
+
signal: invocationSignal,
|
|
1358
|
+
emitted,
|
|
1359
|
+
...step === void 0 ? {} : { step }
|
|
1360
|
+
})
|
|
1361
|
+
),
|
|
1362
|
+
(session2) => Effect_exports.sync(session2.close)
|
|
1363
|
+
);
|
|
1364
|
+
const deadline = context.deadline === void 0 ? void 0 : yield* safe2(
|
|
1365
|
+
() => Schema_exports.decodeUnknownEffect(InvocationDeadline)(context.deadline),
|
|
1366
|
+
new HostInputInvalid()
|
|
1367
|
+
);
|
|
1368
|
+
const withinDeadline = (work) => Effect_exports.gen(function* () {
|
|
1369
|
+
if (deadline === void 0) return yield* work;
|
|
1370
|
+
const remaining = deadline - (yield* Clock_exports.currentTimeMillis);
|
|
1371
|
+
if (remaining <= 0)
|
|
1372
|
+
return yield* new WorkflowFailure({ reason: "engine", retryable: true });
|
|
1373
|
+
const result = yield* work.pipe(
|
|
1374
|
+
Effect_exports.timeout(remaining),
|
|
1375
|
+
Effect_exports.catchTag(
|
|
1376
|
+
"TimeoutError",
|
|
1377
|
+
() => Effect_exports.fail(new WorkflowFailure({ reason: "engine", retryable: true }))
|
|
1378
|
+
)
|
|
1379
|
+
);
|
|
1380
|
+
if ((yield* Clock_exports.currentTimeMillis) >= deadline)
|
|
1381
|
+
return yield* new WorkflowFailure({ reason: "engine", retryable: true });
|
|
1382
|
+
return result;
|
|
1383
|
+
});
|
|
1384
|
+
if (request.operation === "account-check")
|
|
1385
|
+
return yield* checkAccount(
|
|
1386
|
+
native.accounts,
|
|
1387
|
+
declared,
|
|
1388
|
+
request.requirement,
|
|
1389
|
+
context,
|
|
1390
|
+
invocationSignal,
|
|
1391
|
+
deadline
|
|
1392
|
+
).pipe(withinDeadline);
|
|
1393
|
+
let running;
|
|
1394
|
+
const delivery = (request2, signal2) => Effect_exports.suspend(
|
|
1395
|
+
() => running !== void 0 && context.elicitation !== void 0 ? context.elicitation(request2, signal2).pipe(
|
|
1396
|
+
// Delivering the question and waiting for its answer is the person's time.
|
|
1397
|
+
owned("person", "app.tool.elicitation"),
|
|
1398
|
+
Effect_exports.provideContext(running.context)
|
|
1399
|
+
) : Effect_exports.fail(new ElicitationFailed({ reason: "unavailable" }))
|
|
1400
|
+
);
|
|
1401
|
+
const unavailableWorkflow = () => Effect_exports.fail(new WorkflowFailure({ reason: "unavailable", retryable: false }));
|
|
1402
|
+
const telemetry = yield* captureTelemetry;
|
|
1403
|
+
const control = (operation, work) => work.pipe(
|
|
1404
|
+
owned("executor", workflowControlSpan, {
|
|
1405
|
+
attributes: { "executor.workflow.operation": operation }
|
|
1406
|
+
})
|
|
1407
|
+
);
|
|
1408
|
+
const workflowControls = {
|
|
1409
|
+
start: toPromise(
|
|
1410
|
+
(input2) => control(
|
|
1411
|
+
"start",
|
|
1412
|
+
context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.start(input2)
|
|
1413
|
+
),
|
|
1414
|
+
invocationSignal,
|
|
1415
|
+
telemetry.context
|
|
1416
|
+
),
|
|
1417
|
+
get: toPromise(
|
|
1418
|
+
(input2) => control(
|
|
1419
|
+
"get",
|
|
1420
|
+
context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.get(input2)
|
|
1421
|
+
),
|
|
1422
|
+
invocationSignal,
|
|
1423
|
+
telemetry.context
|
|
1424
|
+
),
|
|
1425
|
+
list: toPromise(
|
|
1426
|
+
(input2) => control(
|
|
1427
|
+
"list",
|
|
1428
|
+
context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.list(input2)
|
|
1429
|
+
),
|
|
1430
|
+
invocationSignal,
|
|
1431
|
+
telemetry.context
|
|
1432
|
+
),
|
|
1433
|
+
terminate: toPromise(
|
|
1434
|
+
(input2) => control(
|
|
1435
|
+
"terminate",
|
|
1436
|
+
context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.terminate(input2)
|
|
1437
|
+
),
|
|
1438
|
+
invocationSignal,
|
|
1439
|
+
telemetry.context
|
|
1440
|
+
)
|
|
1441
|
+
};
|
|
1442
|
+
const workflowReads = {
|
|
1443
|
+
get: workflowControls.get,
|
|
1444
|
+
list: workflowControls.list
|
|
1445
|
+
};
|
|
1446
|
+
const files = yield* Schema_exports.decodeUnknownEffect(Schema_exports.Array(SkillFile))(
|
|
1447
|
+
context.files ?? []
|
|
1448
|
+
).pipe(Effect_exports.mapError(() => new HostDeclarationInvalid()));
|
|
1449
|
+
let cacheCommands = 0;
|
|
1450
|
+
const hostCache = context.cache ?? unavailableCache;
|
|
1451
|
+
const fetching = yield* fetchLifetime(signal, hostCache.background);
|
|
1452
|
+
const bound = {
|
|
1453
|
+
cache: authorCache(
|
|
1454
|
+
{
|
|
1455
|
+
...hostCache,
|
|
1456
|
+
transport: (command) => {
|
|
1457
|
+
cacheCommands += 1;
|
|
1458
|
+
return hostCache.transport(command);
|
|
1459
|
+
},
|
|
1460
|
+
background: fetching.background
|
|
1461
|
+
},
|
|
1462
|
+
Redacted_exports.value(context.accounts),
|
|
1463
|
+
invocationSignal,
|
|
1464
|
+
telemetry.context,
|
|
1465
|
+
deadline
|
|
1466
|
+
),
|
|
1467
|
+
files,
|
|
1468
|
+
...yield* bindAccounts(native.accounts, declared, context).pipe(
|
|
1469
|
+
Effect_exports.withSpan("app.accounts.bind")
|
|
1470
|
+
),
|
|
1471
|
+
workflows: workflowReads,
|
|
1472
|
+
signal: invocationSignal,
|
|
1473
|
+
fetch: yield* appInvocationFetch(fetching.signal),
|
|
1474
|
+
elicit: makeElicit(delivery, invocationSignal, telemetry.context)
|
|
1475
|
+
};
|
|
1476
|
+
const attributable = userAccountIds(bound);
|
|
1477
|
+
const accountIds = [
|
|
1478
|
+
...Object.values(bound.app.accounts).flatMap(
|
|
1479
|
+
(selected) => (Array.isArray(selected) ? selected : [selected]).map(
|
|
1480
|
+
(account) => account.id
|
|
1481
|
+
)
|
|
1482
|
+
),
|
|
1483
|
+
...attributable
|
|
1484
|
+
];
|
|
1485
|
+
const emitter = (sourceAccount) => makeEmitter({
|
|
1486
|
+
events: native.events ?? {},
|
|
1487
|
+
accounts: accountIds,
|
|
1488
|
+
attributable,
|
|
1489
|
+
...sourceAccount === void 0 ? {} : { sourceAccount },
|
|
1490
|
+
emitted
|
|
1491
|
+
});
|
|
1492
|
+
const definition = yield* evaluationSafe(native.evaluate(bound), secrets).pipe(
|
|
1493
|
+
Effect_exports.withSpan("app.evaluate")
|
|
1494
|
+
);
|
|
1495
|
+
if (request.operation === "skills") {
|
|
1496
|
+
const declared2 = definition.skills ?? (yield* folderSkillsEffect({ files }).pipe(
|
|
1497
|
+
Effect_exports.mapError(() => new HostDeclarationInvalid())
|
|
1498
|
+
));
|
|
1499
|
+
const source = definition.dynamicSkills;
|
|
1500
|
+
const before = cacheCommands;
|
|
1501
|
+
const dynamic = source === void 0 ? [] : yield* evaluationSafe(Effect_exports.suspend(source.list), secrets).pipe(
|
|
1502
|
+
Effect_exports.withSpan("app.skills.load")
|
|
1503
|
+
);
|
|
1504
|
+
const cached = source !== void 0 && cacheCommands > before;
|
|
1505
|
+
const authored = new Set([...declared2, ...dynamic].map((skill) => skill.name));
|
|
1506
|
+
const routed = (yield* routerSkills(definition.tools).pipe(
|
|
1507
|
+
Effect_exports.withSpan("app.skills.routers")
|
|
1508
|
+
)).filter((skill) => !authored.has(skill.name));
|
|
1509
|
+
const skills = yield* Schema_exports.decodeUnknownEffect(AppSkills)([
|
|
1510
|
+
...declared2,
|
|
1511
|
+
...dynamic,
|
|
1512
|
+
...routed
|
|
1513
|
+
]).pipe(Effect_exports.mapError(() => new HostDeclarationInvalid()));
|
|
1514
|
+
return request.sources === true ? { skills, dynamic: source !== void 0, cached } : skills;
|
|
1515
|
+
}
|
|
1516
|
+
if (request.operation === "workflows") {
|
|
1517
|
+
return yield* Effect_exports.forEach(
|
|
1518
|
+
Object.entries(definition.workflows ?? {}),
|
|
1519
|
+
([name, entry]) => safe2(
|
|
1520
|
+
() => Effect_exports.gen(function* () {
|
|
1521
|
+
return yield* Schema_exports.decodeUnknownEffect(HostedWorkflow)({
|
|
1522
|
+
name,
|
|
1523
|
+
...entry.description === void 0 ? {} : { description: entry.description },
|
|
1524
|
+
inputSchema: yield* jsonSchemaDocument(entry.input),
|
|
1525
|
+
...entry.output === void 0 ? {} : { outputSchema: yield* jsonSchemaDocument(entry.output) }
|
|
1526
|
+
});
|
|
1527
|
+
}),
|
|
1528
|
+
new HostDeclarationInvalid()
|
|
1529
|
+
)
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
if (request.operation === "workflow-validate" || request.operation === "workflow-run") {
|
|
1533
|
+
const entry = definition.workflows !== void 0 && Object.hasOwn(definition.workflows, request.name) ? definition.workflows[request.name] : void 0;
|
|
1534
|
+
if (entry === void 0)
|
|
1535
|
+
return yield* new WorkflowFailure({ reason: "not_found", retryable: false });
|
|
1536
|
+
const input2 = yield* safe2(
|
|
1537
|
+
() => Schema_exports.decodeUnknownEffect(entry.input)(request.input),
|
|
1538
|
+
new WorkflowFailure({ reason: "input", retryable: false })
|
|
1539
|
+
);
|
|
1540
|
+
if (request.operation === "workflow-validate")
|
|
1541
|
+
return yield* safe2(
|
|
1542
|
+
() => Schema_exports.decodeUnknownEffect(WorkflowValue)(input2),
|
|
1543
|
+
new WorkflowFailure({ reason: "input", retryable: false })
|
|
1544
|
+
);
|
|
1545
|
+
const execution = context.workflow;
|
|
1546
|
+
if (execution === void 0)
|
|
1547
|
+
return yield* new WorkflowFailure({ reason: "unavailable", retryable: false });
|
|
1548
|
+
const workflowContext = yield* makeWorkflowContext(
|
|
1549
|
+
execution,
|
|
1550
|
+
definition,
|
|
1551
|
+
(stepId, signal2) => Effect_exports.gen(function* () {
|
|
1552
|
+
const current = yield* execution.resolve();
|
|
1553
|
+
const accounts = yield* safe2(
|
|
1554
|
+
() => bindAccounts(native.accounts, declared, current),
|
|
1555
|
+
new WorkflowFailure({ reason: "credentials", retryable: false })
|
|
1556
|
+
);
|
|
1557
|
+
return {
|
|
1558
|
+
context: {
|
|
1559
|
+
...accounts,
|
|
1560
|
+
cache: authorCache(
|
|
1561
|
+
context.cache ?? unavailableCache,
|
|
1562
|
+
Redacted_exports.value(current.accounts),
|
|
1563
|
+
signal2,
|
|
1564
|
+
(yield* captureTelemetry).context
|
|
1565
|
+
),
|
|
1566
|
+
files,
|
|
1567
|
+
fetch: yield* appInvocationFetch(signal2),
|
|
1568
|
+
signal: signal2,
|
|
1569
|
+
runId: execution.runId,
|
|
1570
|
+
stepId,
|
|
1571
|
+
idempotencyKey: stepId
|
|
1572
|
+
},
|
|
1573
|
+
// The resolved values, not the app's decoded fields, which may be `Redacted`.
|
|
1574
|
+
secrets: accountSecrets(current.accounts)
|
|
1575
|
+
};
|
|
1576
|
+
}),
|
|
1577
|
+
invocationSignal
|
|
1578
|
+
);
|
|
1579
|
+
const output = yield* workflowSafe(entry.run(workflowContext, input2), secrets);
|
|
1580
|
+
const outputSchema = entry.output;
|
|
1581
|
+
const decoded = outputSchema === void 0 ? output : yield* safe2(
|
|
1582
|
+
() => Schema_exports.decodeUnknownEffect(outputSchema)(output),
|
|
1583
|
+
new WorkflowFailure({
|
|
1584
|
+
reason: "output",
|
|
1585
|
+
retryable: false,
|
|
1586
|
+
message: "The workflow's result does not match its declared output schema."
|
|
1587
|
+
})
|
|
1588
|
+
);
|
|
1589
|
+
return yield* safe2(
|
|
1590
|
+
() => Schema_exports.decodeUnknownEffect(WorkflowValue)(decoded),
|
|
1591
|
+
new WorkflowFailure({
|
|
1592
|
+
reason: "output",
|
|
1593
|
+
retryable: false,
|
|
1594
|
+
message: "The workflow returned a value that is not JSON, such as undefined, or is larger than 1 MiB. Return null for no result."
|
|
1595
|
+
})
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
if (request.operation === "inspect") {
|
|
1599
|
+
const summary = request.detail === "summary";
|
|
1600
|
+
const catalog = yield* readCatalog(definition.tools, {
|
|
1601
|
+
summary,
|
|
1602
|
+
secrets,
|
|
1603
|
+
...request.tools === void 0 ? {} : { wanted: new Set(request.tools) },
|
|
1604
|
+
...request.scheduled === true ? { scheduled: true } : {},
|
|
1605
|
+
schedules: (name) => Object.entries(definition.schedules ?? {}).filter(([, schedule]) => schedule.tool === name).map(([name2, { tool: _tool, ...schedule }]) => ({ name: name2, ...schedule }))
|
|
1606
|
+
}).pipe(Effect_exports.withSpan("app.catalog.read"));
|
|
1607
|
+
return yield* safe2(
|
|
1608
|
+
() => Schema_exports.encodeEffect(WireCatalog)(catalog).pipe(
|
|
1609
|
+
Effect_exports.flatMap(Schema_exports.decodeUnknownEffect(JsonValue))
|
|
1610
|
+
),
|
|
1611
|
+
new HostDeclarationInvalid()
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
if (request.operation === "webhook-complete" || request.operation === "webhook-validate" || request.operation === "webhooks" || request.operation === "webhook-register" || request.operation === "webhook-handle" || request.operation === "webhook-unregister") {
|
|
1615
|
+
const session2 = yield* sqlSession();
|
|
1616
|
+
return yield* dispatchWebhook(
|
|
1617
|
+
definition,
|
|
1618
|
+
request,
|
|
1619
|
+
{
|
|
1620
|
+
files,
|
|
1621
|
+
cache: bound.cache,
|
|
1622
|
+
app: bound.app,
|
|
1623
|
+
...bound.user === void 0 ? {} : { user: bound.user },
|
|
1624
|
+
workflows: workflowControls,
|
|
1625
|
+
signal: bound.signal,
|
|
1626
|
+
fetch: bound.fetch,
|
|
1627
|
+
sql: session2.writer,
|
|
1628
|
+
events: emitter("sourceAccount" in request ? request.sourceAccount : void 0)
|
|
1629
|
+
},
|
|
1630
|
+
native.accounts
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
const toolName = request.operation === "call" ? request.tool : request.name;
|
|
1634
|
+
const location = locate(definition.tools, toolName);
|
|
1635
|
+
const tool = location === void 0 ? void 0 : location.kind === "operation" ? location.operation : yield* evaluationSafe(location.source.resolve(location.name), secrets).pipe(
|
|
1636
|
+
Effect_exports.withSpan("app.tool.resolve")
|
|
1637
|
+
);
|
|
1638
|
+
if (tool === void 0)
|
|
1639
|
+
return yield* request.operation === "call" ? new HostToolNotFound() : new HostOperationNotFound();
|
|
1640
|
+
const requested = request.operation === "call" ? request.kind : request.operation === "query" ? "query" : "mutation";
|
|
1641
|
+
if (requested !== void 0 && tool.kind !== requested)
|
|
1642
|
+
return yield* new HostKindMismatch({ tool: toolName, requested, actual: tool.kind });
|
|
1643
|
+
const kind = tool.kind === "query" ? "query" : "mutate";
|
|
1644
|
+
const input = yield* Effect_exports.suspend(
|
|
1645
|
+
() => Schema_exports.decodeUnknownEffect(tool.input)(request.input)
|
|
1646
|
+
).pipe(
|
|
1647
|
+
Effect_exports.catchCause(
|
|
1648
|
+
(cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.fail(inputInvalid(Cause_exports.squash(cause), request.input))
|
|
1649
|
+
)
|
|
1650
|
+
);
|
|
1651
|
+
if (context.approval !== void 0) {
|
|
1652
|
+
const approved = yield* safe2(
|
|
1653
|
+
() => Schema_exports.decodeUnknownEffect(TrustedToolApproval)(context.approval),
|
|
1654
|
+
new HostInputInvalid()
|
|
1655
|
+
);
|
|
1656
|
+
const decoded = yield* safe2(
|
|
1657
|
+
() => Schema_exports.decodeUnknownEffect(JsonValue)(input),
|
|
1658
|
+
new HostInputInvalid()
|
|
1659
|
+
);
|
|
1660
|
+
if (approved.tool !== toolName || !Schema_exports.toEquivalence(JsonValue)(approved.input, decoded)) {
|
|
1661
|
+
return yield* new HostInputInvalid();
|
|
1662
|
+
}
|
|
1663
|
+
} else if (tool.approval !== void 0) {
|
|
1664
|
+
const approval = tool.approval;
|
|
1665
|
+
const decision = yield* safe2(
|
|
1666
|
+
() => approval({ toolName, toolInput: input, signal: invocationSignal }).pipe(
|
|
1667
|
+
Effect_exports.withSpan("app.tool.approval", {
|
|
1668
|
+
attributes: { "executor.tool.name": toolName }
|
|
1669
|
+
}),
|
|
1670
|
+
Effect_exports.flatMap(Schema_exports.decodeUnknownEffect(ApprovalDecision))
|
|
1671
|
+
),
|
|
1672
|
+
new HostToolPolicyFailed()
|
|
1673
|
+
);
|
|
1674
|
+
yield* Match_exports.value(decision).pipe(
|
|
1675
|
+
Match_exports.when("approved", () => Effect_exports.void),
|
|
1676
|
+
Match_exports.when("denied", () => Effect_exports.fail(new HostToolBlocked())),
|
|
1677
|
+
Match_exports.when(
|
|
1678
|
+
"user-approval",
|
|
1679
|
+
() => safe2(() => Schema_exports.decodeUnknownEffect(JsonValue)(input), new HostInputInvalid()).pipe(
|
|
1680
|
+
Effect_exports.flatMap(
|
|
1681
|
+
(input2) => Effect_exports.fail(
|
|
1682
|
+
new HostToolApprovalRequired({
|
|
1683
|
+
input: input2,
|
|
1684
|
+
elicitation: approvalElicitation(toolName, input2)
|
|
1685
|
+
})
|
|
1686
|
+
)
|
|
1687
|
+
)
|
|
1688
|
+
)
|
|
1689
|
+
),
|
|
1690
|
+
Match_exports.exhaustive
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
const replay = context.replay === void 0 ? void 0 : yield* safe2(
|
|
1694
|
+
() => Schema_exports.decodeUnknownEffect(WorkflowReplay)(context.replay),
|
|
1695
|
+
new HostInputInvalid()
|
|
1696
|
+
);
|
|
1697
|
+
if (replay !== void 0 && kind !== "mutate") return yield* new HostInputInvalid();
|
|
1698
|
+
const session = yield* sqlSession(replay);
|
|
1699
|
+
const execute = () => Effect_exports.gen(function* () {
|
|
1700
|
+
const output = yield* Effect_exports.gen(function* () {
|
|
1701
|
+
running = yield* captureTelemetry;
|
|
1702
|
+
const fetch = yield* appInvocationFetch(fetching.signal);
|
|
1703
|
+
return yield* tool.run(
|
|
1704
|
+
{
|
|
1705
|
+
...bound,
|
|
1706
|
+
fetch,
|
|
1707
|
+
workflows: kind === "mutate" ? workflowControls : workflowReads,
|
|
1708
|
+
sql: kind === "mutate" ? session.writer : session.reader,
|
|
1709
|
+
...kind === "mutate" ? { events: emitter() } : {}
|
|
1710
|
+
},
|
|
1711
|
+
input
|
|
1712
|
+
);
|
|
1713
|
+
}).pipe(
|
|
1714
|
+
Effect_exports.ensuring(
|
|
1715
|
+
Effect_exports.sync(() => {
|
|
1716
|
+
running = void 0;
|
|
1717
|
+
})
|
|
1718
|
+
),
|
|
1719
|
+
Effect_exports.catchCause((cause) => {
|
|
1720
|
+
if (Cause_exports.hasInterrupts(cause)) return Effect_exports.interrupt;
|
|
1721
|
+
const error = Cause_exports.squash(cause);
|
|
1722
|
+
const provider = parseProviderError(error);
|
|
1723
|
+
const response = Schema_exports.decodeUnknownOption(OpenapiResponseError)(error);
|
|
1724
|
+
const mcp = parseMcpError(error, secrets);
|
|
1725
|
+
const failure = Schema_exports.decodeUnknownOption(ElicitationFailed)(error);
|
|
1726
|
+
const removed = Schema_exports.is(HostToolNotFound)(error) ? request.operation === "call" ? new HostToolNotFound() : new HostOperationNotFound() : void 0;
|
|
1727
|
+
return Effect_exports.fail(
|
|
1728
|
+
removed !== void 0 ? removed : Option_exports.isSome(provider) ? leavingProviderError(provider.value, secrets, "call") : Option_exports.isSome(response) ? new OpenapiResponseError({
|
|
1729
|
+
code: response.value.code,
|
|
1730
|
+
status: response.value.status,
|
|
1731
|
+
message: response.value.message,
|
|
1732
|
+
...response.value.recovery === void 0 ? {} : { recovery: response.value.recovery }
|
|
1733
|
+
}) : Option_exports.isSome(mcp) ? mcp.value : Option_exports.isSome(failure) ? failure.value : new HostOperationFailed(failureDetail(error, secrets))
|
|
1734
|
+
);
|
|
1735
|
+
}),
|
|
1736
|
+
Effect_exports.withSpan("app.operation.execute", {
|
|
1737
|
+
attributes: { "executor.tool.name": toolName, "executor.operation": kind }
|
|
1738
|
+
})
|
|
1739
|
+
);
|
|
1740
|
+
const outputSchema = tool.output;
|
|
1741
|
+
const decoded = outputSchema === void 0 ? output : yield* safe2(
|
|
1742
|
+
() => Schema_exports.decodeUnknownEffect(outputSchema)(output),
|
|
1743
|
+
new HostOutputInvalid()
|
|
1744
|
+
);
|
|
1745
|
+
return yield* safe2(
|
|
1746
|
+
() => Schema_exports.decodeUnknownEffect(JsonValue)(decoded),
|
|
1747
|
+
new HostOutputInvalid()
|
|
1748
|
+
);
|
|
1749
|
+
});
|
|
1750
|
+
return yield* withinDeadline(execute());
|
|
1751
|
+
})
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
function checkAccount(slots, declared, requirement, context, signal, deadline) {
|
|
1755
|
+
return Effect_exports.gen(function* () {
|
|
1756
|
+
const scope = Object.hasOwn(slots.app, requirement) ? "app" : Object.hasOwn(slots.user, requirement) ? "user" : void 0;
|
|
1757
|
+
if (scope === void 0) return yield* new HostAccountsInvalid();
|
|
1758
|
+
const selection = slots[scope][requirement];
|
|
1759
|
+
const slot = Object.hasOwn(declared.accounts[scope], requirement) ? declared.accounts[scope][requirement] : void 0;
|
|
1760
|
+
if (selection === void 0 || slot === void 0) return yield* new HostAccountsInvalid();
|
|
1761
|
+
const provider = selection instanceof ManyAccounts ? selection.provider : selection;
|
|
1762
|
+
const health = provider.health;
|
|
1763
|
+
if (health === void 0) return yield* new HostOperationNotFound();
|
|
1764
|
+
const accounts = yield* safe2(
|
|
1765
|
+
() => Effect_exports.gen(function* () {
|
|
1766
|
+
const selected = yield* Schema_exports.decodeUnknownEffect(ResolvedAccounts)(
|
|
1767
|
+
Redacted_exports.value(context.accounts)
|
|
1768
|
+
);
|
|
1769
|
+
const supplied = selected[scope];
|
|
1770
|
+
if (supplied === void 0) return yield* new HostAccountsInvalid();
|
|
1771
|
+
return yield* bindScope(
|
|
1772
|
+
{ [requirement]: provider },
|
|
1773
|
+
{ [requirement]: { ...slot, cardinality: "one" } },
|
|
1774
|
+
supplied
|
|
1775
|
+
);
|
|
1776
|
+
}),
|
|
1777
|
+
new HostAccountsInvalid()
|
|
1778
|
+
).pipe(Effect_exports.withSpan("app.accounts.bind"));
|
|
1779
|
+
const account = accounts[requirement];
|
|
1780
|
+
if (account === void 0 || !("id" in account)) return yield* new HostAccountsInvalid();
|
|
1781
|
+
const result = yield* Effect_exports.suspend(
|
|
1782
|
+
() => Effect_exports.gen(function* () {
|
|
1783
|
+
return yield* health.run({
|
|
1784
|
+
account,
|
|
1785
|
+
fetch: yield* appInvocationFetch(signal),
|
|
1786
|
+
signal,
|
|
1787
|
+
...deadline === void 0 ? {} : { deadline }
|
|
1788
|
+
});
|
|
1789
|
+
})
|
|
1790
|
+
).pipe(
|
|
1791
|
+
Effect_exports.catchCause((cause) => {
|
|
1792
|
+
if (Cause_exports.hasInterrupts(cause)) return Effect_exports.interrupt;
|
|
1793
|
+
const error = Cause_exports.squash(cause);
|
|
1794
|
+
const status = Schema_exports.decodeUnknownOption(ResponseStatusError)(error);
|
|
1795
|
+
const classified = Option_exports.isSome(status) ? Option_exports.fromNullishOr(httpProviderError(status.value.status)) : parseProviderError(error);
|
|
1796
|
+
const secrets = accountSecrets(context.accounts);
|
|
1797
|
+
return Effect_exports.fail(
|
|
1798
|
+
Option_exports.isSome(classified) ? leavingProviderError(accountProviderError(classified.value, account.id), secrets) : new HostOperationFailed(failureDetail(error, secrets))
|
|
1799
|
+
);
|
|
1800
|
+
}),
|
|
1801
|
+
Effect_exports.withSpan("app.account.check")
|
|
1802
|
+
);
|
|
1803
|
+
return yield* safe2(
|
|
1804
|
+
() => Schema_exports.decodeUnknownEffect(AccountCheckResult)(result ?? {}).pipe(
|
|
1805
|
+
Effect_exports.flatMap(Schema_exports.encodeEffect(AccountCheckResult)),
|
|
1806
|
+
Effect_exports.flatMap(Schema_exports.decodeUnknownEffect(JsonValue))
|
|
1807
|
+
),
|
|
1808
|
+
new HostOutputInvalid()
|
|
1809
|
+
);
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
var errorStatus = Match_exports.type().pipe(
|
|
1813
|
+
Match_exports.tagsExhaustive({
|
|
1814
|
+
ProviderError: () => 502,
|
|
1815
|
+
McpError: () => 502,
|
|
1816
|
+
SkillLoadFailed: () => 502,
|
|
1817
|
+
OpenapiResponseError: () => 502,
|
|
1818
|
+
WorkflowFailure: () => 422,
|
|
1819
|
+
HostRequestInvalid: () => 400,
|
|
1820
|
+
HostAccountsInvalid: () => 422,
|
|
1821
|
+
HostInputInvalid: () => 422,
|
|
1822
|
+
HostOperationNotFound: () => 404,
|
|
1823
|
+
HostKindMismatch: () => 409,
|
|
1824
|
+
HostToolNotFound: () => 404,
|
|
1825
|
+
HostToolBlocked: () => 403,
|
|
1826
|
+
HostToolApprovalRequired: () => 409,
|
|
1827
|
+
HostToolPolicyFailed: () => 500,
|
|
1828
|
+
HostOperationFailed: () => 500,
|
|
1829
|
+
DatabaseLimitExceeded: () => 422,
|
|
1830
|
+
DatabaseFieldReserved: () => 422,
|
|
1831
|
+
HostDeclarationInvalid: () => 500,
|
|
1832
|
+
HostEvaluationFailed: () => 500,
|
|
1833
|
+
HostOutputInvalid: () => 500,
|
|
1834
|
+
ElicitationFailed: () => 422
|
|
1835
|
+
})
|
|
1836
|
+
);
|
|
1837
|
+
var createAppHandler = (app) => (request, context) => Effect_exports.gen(function* () {
|
|
1838
|
+
if (request.method !== "POST") return yield* Effect_exports.fail(new HostRequestInvalid());
|
|
1839
|
+
const input = yield* Effect_exports.tryPromise({
|
|
1840
|
+
try: () => request.json(),
|
|
1841
|
+
catch: () => new HostRequestInvalid()
|
|
1842
|
+
});
|
|
1843
|
+
const command = yield* Schema_exports.decodeUnknownEffect(HostRequest)(input, {
|
|
1844
|
+
onExcessProperty: "error"
|
|
1845
|
+
}).pipe(Effect_exports.mapError(() => new HostRequestInvalid()));
|
|
1846
|
+
yield* Effect_exports.annotateCurrentSpan({
|
|
1847
|
+
"executor.operation": command.operation,
|
|
1848
|
+
...command.operation === "call" ? { "executor.tool.name": command.tool } : {}
|
|
1849
|
+
});
|
|
1850
|
+
let toolError = false;
|
|
1851
|
+
const emitted = [];
|
|
1852
|
+
const value = yield* dispatch(app, command, context, request.signal, emitted).pipe(
|
|
1853
|
+
Effect_exports.provideService(ToolResultObservation, {
|
|
1854
|
+
failed: () => {
|
|
1855
|
+
toolError = true;
|
|
1856
|
+
}
|
|
1857
|
+
}),
|
|
1858
|
+
// Name what failed on the span; the bounded message stays in the reply only. The host
|
|
1859
|
+
// records an app's own error names and codes as `unrecognized` (telemetry `app-records`).
|
|
1860
|
+
Effect_exports.tapError(
|
|
1861
|
+
(error) => (error._tag === "HostEvaluationFailed" || error._tag === "HostOperationFailed" || error._tag === "HostDeclarationInvalid") && error.errorName !== void 0 ? Effect_exports.annotateCurrentSpan({
|
|
1862
|
+
"error.type": error.errorName,
|
|
1863
|
+
...error.source === void 0 ? {} : { "executor.failure.source": error.source },
|
|
1864
|
+
...error.code === void 0 ? {} : { "executor.failure.code": error.code }
|
|
1865
|
+
}) : Effect_exports.void
|
|
1866
|
+
),
|
|
1867
|
+
// Tool operations time themselves, dividing their time by who owned the work in progress.
|
|
1868
|
+
// Other operations do not.
|
|
1869
|
+
command.operation === "call" || command.operation === "query" || command.operation === "mutate" ? timedInvocation(command.operation) : Effect_exports.withSpan(`app.${command.operation}`)
|
|
1870
|
+
);
|
|
1871
|
+
if (toolError)
|
|
1872
|
+
yield* Effect_exports.annotateCurrentSpan({
|
|
1873
|
+
"executor.outcome": "failed",
|
|
1874
|
+
"error.type": "McpToolError"
|
|
1875
|
+
});
|
|
1876
|
+
return Response.json({
|
|
1877
|
+
ok: true,
|
|
1878
|
+
value,
|
|
1879
|
+
...toolError ? { toolError: true } : {},
|
|
1880
|
+
...emitted.length === 0 ? {} : { events: emitted }
|
|
1881
|
+
});
|
|
1882
|
+
}).pipe(
|
|
1883
|
+
Effect_exports.catch(
|
|
1884
|
+
(error) => Schema_exports.encodeEffect(HostError)(error).pipe(
|
|
1885
|
+
Effect_exports.map(
|
|
1886
|
+
(encoded) => Response.json({ ok: false, error: encoded }, { status: errorStatus(error) })
|
|
1887
|
+
),
|
|
1888
|
+
Effect_exports.orDie
|
|
1889
|
+
)
|
|
1890
|
+
),
|
|
1891
|
+
Effect_exports.catchCause(
|
|
1892
|
+
(cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : (
|
|
1893
|
+
// The cause can carry any text the app threw; its caller receives that, not the log.
|
|
1894
|
+
Effect_exports.logError("The app's request failed unexpectedly").pipe(
|
|
1895
|
+
Effect_exports.andThen(
|
|
1896
|
+
Schema_exports.encodeEffect(HostError)(
|
|
1897
|
+
declarationInvalid(
|
|
1898
|
+
"The app failed while it was loaded",
|
|
1899
|
+
Cause_exports.squash(cause),
|
|
1900
|
+
accountSecrets(context.accounts)
|
|
1901
|
+
)
|
|
1902
|
+
)
|
|
1903
|
+
),
|
|
1904
|
+
Effect_exports.map((error) => Response.json({ ok: false, error }, { status: 500 })),
|
|
1905
|
+
Effect_exports.orDie
|
|
1906
|
+
)
|
|
1907
|
+
)
|
|
1908
|
+
)
|
|
1909
|
+
);
|
|
1910
|
+
|
|
1911
|
+
export {
|
|
1912
|
+
prepareElicitation,
|
|
1913
|
+
ApprovalDecision,
|
|
1914
|
+
createAppHandler
|
|
1915
|
+
};
|