@occam-scaly/mcp-server 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -0
- package/bin/scaly-mcp.js +6 -0
- package/dist/audit.d.ts +25 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +182 -0
- package/dist/audit.js.map +1 -0
- package/dist/client.d.ts +16 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +142 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +76 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +279 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/index.d.ts +17 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +505 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/tools/addons.d.ts +3 -0
- package/dist/tools/addons.d.ts.map +1 -0
- package/dist/tools/addons.js +128 -0
- package/dist/tools/addons.js.map +1 -0
- package/dist/tools/apps.d.ts +3 -0
- package/dist/tools/apps.d.ts.map +1 -0
- package/dist/tools/apps.js +121 -0
- package/dist/tools/apps.js.map +1 -0
- package/dist/tools/compute.d.ts +3 -0
- package/dist/tools/compute.d.ts.map +1 -0
- package/dist/tools/compute.js +135 -0
- package/dist/tools/compute.js.map +1 -0
- package/dist/tools/db.d.ts +3 -0
- package/dist/tools/db.d.ts.map +1 -0
- package/dist/tools/db.js +597 -0
- package/dist/tools/db.js.map +1 -0
- package/dist/tools/deployments.d.ts +3 -0
- package/dist/tools/deployments.d.ts.map +1 -0
- package/dist/tools/deployments.js +354 -0
- package/dist/tools/deployments.js.map +1 -0
- package/dist/tools/diagnose.d.ts +3 -0
- package/dist/tools/diagnose.d.ts.map +1 -0
- package/dist/tools/diagnose.js +213 -0
- package/dist/tools/diagnose.js.map +1 -0
- package/dist/tools/env.d.ts +3 -0
- package/dist/tools/env.d.ts.map +1 -0
- package/dist/tools/env.js +216 -0
- package/dist/tools/env.js.map +1 -0
- package/dist/tools/index.d.ts +38 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +48 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/jobs.d.ts +3 -0
- package/dist/tools/jobs.d.ts.map +1 -0
- package/dist/tools/jobs.js +160 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/logs.d.ts +3 -0
- package/dist/tools/logs.d.ts.map +1 -0
- package/dist/tools/logs.js +157 -0
- package/dist/tools/logs.js.map +1 -0
- package/dist/tools/operations.d.ts +3 -0
- package/dist/tools/operations.d.ts.map +1 -0
- package/dist/tools/operations.js +262 -0
- package/dist/tools/operations.js.map +1 -0
- package/dist/tools/project.d.ts +3 -0
- package/dist/tools/project.d.ts.map +1 -0
- package/dist/tools/project.js +341 -0
- package/dist/tools/project.js.map +1 -0
- package/dist/tools/s3.d.ts +3 -0
- package/dist/tools/s3.d.ts.map +1 -0
- package/dist/tools/s3.js +256 -0
- package/dist/tools/s3.js.map +1 -0
- package/dist/tools/status.d.ts +3 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/status.js +139 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/write.d.ts +3 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +900 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/utils/jwt.d.ts +3 -0
- package/dist/utils/jwt.d.ts.map +1 -0
- package/dist/utils/jwt.js +32 -0
- package/dist/utils/jwt.js.map +1 -0
- package/dist/utils/paths.d.ts +5 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +26 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,900 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeTools = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const client_js_1 = require("../client.js");
|
|
6
|
+
const index_js_1 = require("./index.js");
|
|
7
|
+
function estimateDbMonthlyUsd(size) {
|
|
8
|
+
return ({
|
|
9
|
+
Micro: 10,
|
|
10
|
+
Small: 20,
|
|
11
|
+
Medium: 40,
|
|
12
|
+
Large: 80,
|
|
13
|
+
XLarge: 160
|
|
14
|
+
}[size] ?? 20);
|
|
15
|
+
}
|
|
16
|
+
function estimateStackMonthlyUsd(size) {
|
|
17
|
+
return ({
|
|
18
|
+
Eco: 8,
|
|
19
|
+
Basic: 16,
|
|
20
|
+
Standard_1X: 32,
|
|
21
|
+
Standard_2X: 64,
|
|
22
|
+
Performance_M: 128,
|
|
23
|
+
Performance_L: 256
|
|
24
|
+
}[size] ?? 16);
|
|
25
|
+
}
|
|
26
|
+
async function resolveAccountId(explicit) {
|
|
27
|
+
if (explicit)
|
|
28
|
+
return explicit;
|
|
29
|
+
const result = await (0, client_js_1.query)(`
|
|
30
|
+
query ResolveAccountId {
|
|
31
|
+
listStacks(take: 1) { accountId }
|
|
32
|
+
}
|
|
33
|
+
`);
|
|
34
|
+
return result.listStacks?.[0]?.accountId ?? null;
|
|
35
|
+
}
|
|
36
|
+
async function getStackById(stackId) {
|
|
37
|
+
const result = await (0, client_js_1.query)(`
|
|
38
|
+
query GetStack($where: StackWhereUniqueInput!) {
|
|
39
|
+
getStack(where: $where) { id name accountId size minIdle }
|
|
40
|
+
}
|
|
41
|
+
`, { where: { id: stackId } });
|
|
42
|
+
return result.getStack || null;
|
|
43
|
+
}
|
|
44
|
+
async function getAppById(appId) {
|
|
45
|
+
const result = await (0, client_js_1.query)(`
|
|
46
|
+
query GetApp($where: AppWhereUniqueInput!) {
|
|
47
|
+
getApp(where: $where) { id name accountId stackId }
|
|
48
|
+
}
|
|
49
|
+
`, { where: { id: appId } });
|
|
50
|
+
return result.getApp || null;
|
|
51
|
+
}
|
|
52
|
+
async function getAddOnById(addonId) {
|
|
53
|
+
const result = await (0, client_js_1.query)(`
|
|
54
|
+
query GetAddOn($where: AddOnWhereUniqueInput!) {
|
|
55
|
+
getAddOn(where: $where) { id name type accountId }
|
|
56
|
+
}
|
|
57
|
+
`, { where: { id: addonId } });
|
|
58
|
+
return result.getAddOn || null;
|
|
59
|
+
}
|
|
60
|
+
async function stackExistsByName(accountId, name) {
|
|
61
|
+
const gql = `
|
|
62
|
+
query FindStackByName($accountId: String!, $name: String!) {
|
|
63
|
+
listStacks(
|
|
64
|
+
where: {
|
|
65
|
+
AND: [
|
|
66
|
+
{ accountId: { equals: $accountId } }
|
|
67
|
+
{ name: { equals: $name } }
|
|
68
|
+
{ isDeleted: { equals: false } }
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
take: 5
|
|
72
|
+
) { id name }
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
const result = await (0, client_js_1.query)(gql, { accountId, name });
|
|
76
|
+
return result.listStacks || [];
|
|
77
|
+
}
|
|
78
|
+
async function appExistsByName(accountId, name) {
|
|
79
|
+
const gql = `
|
|
80
|
+
query FindAppByName($accountId: String!, $name: String!) {
|
|
81
|
+
listApps(
|
|
82
|
+
where: {
|
|
83
|
+
AND: [
|
|
84
|
+
{ accountId: { equals: $accountId } }
|
|
85
|
+
{ name: { equals: $name } }
|
|
86
|
+
{ isDeleted: { equals: false } }
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
take: 5
|
|
90
|
+
) { id name stackId }
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
const result = await (0, client_js_1.query)(gql, { accountId, name });
|
|
94
|
+
return result.listApps || [];
|
|
95
|
+
}
|
|
96
|
+
async function addOnExistsByName(accountId, name) {
|
|
97
|
+
const gql = `
|
|
98
|
+
query FindAddOnByName($accountId: String!, $name: String!) {
|
|
99
|
+
listAddOns(
|
|
100
|
+
where: {
|
|
101
|
+
AND: [
|
|
102
|
+
{ accountId: { equals: $accountId } }
|
|
103
|
+
{ name: { equals: $name } }
|
|
104
|
+
{ isDeleted: { equals: false } }
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
take: 5
|
|
108
|
+
) { id name type }
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
const result = await (0, client_js_1.query)(gql, { accountId, name });
|
|
112
|
+
return result.listAddOns || [];
|
|
113
|
+
}
|
|
114
|
+
const CreateStackSchema = zod_1.z.object({
|
|
115
|
+
account_id: zod_1.z
|
|
116
|
+
.string()
|
|
117
|
+
.optional()
|
|
118
|
+
.describe('Account ID. If omitted, Scaly will infer from your accessible stacks.'),
|
|
119
|
+
name: zod_1.z.string().min(1).describe('Stack name'),
|
|
120
|
+
size: zod_1.z
|
|
121
|
+
.enum([
|
|
122
|
+
'Eco',
|
|
123
|
+
'Basic',
|
|
124
|
+
'Standard_1X',
|
|
125
|
+
'Standard_2X',
|
|
126
|
+
'Performance_M',
|
|
127
|
+
'Performance_L'
|
|
128
|
+
])
|
|
129
|
+
.describe('Stack size'),
|
|
130
|
+
region: zod_1.z
|
|
131
|
+
.enum(['CANADA', 'US', 'EU', 'ASIA_PACIFIC'])
|
|
132
|
+
.optional()
|
|
133
|
+
.describe('Informational only (region is derived from account today)'),
|
|
134
|
+
min_idle: zod_1.z
|
|
135
|
+
.number()
|
|
136
|
+
.int()
|
|
137
|
+
.min(0)
|
|
138
|
+
.optional()
|
|
139
|
+
.describe('Minimum warm instances'),
|
|
140
|
+
preview: zod_1.z
|
|
141
|
+
.boolean()
|
|
142
|
+
.default(true)
|
|
143
|
+
.describe('If true, returns what would change without executing')
|
|
144
|
+
});
|
|
145
|
+
const UpdateStackSchema = zod_1.z.object({
|
|
146
|
+
stack_id: zod_1.z.string().describe('Stack ID'),
|
|
147
|
+
size: zod_1.z
|
|
148
|
+
.enum([
|
|
149
|
+
'Eco',
|
|
150
|
+
'Basic',
|
|
151
|
+
'Standard_1X',
|
|
152
|
+
'Standard_2X',
|
|
153
|
+
'Performance_M',
|
|
154
|
+
'Performance_L'
|
|
155
|
+
])
|
|
156
|
+
.optional(),
|
|
157
|
+
min_idle: zod_1.z.number().int().min(0).optional(),
|
|
158
|
+
preview: zod_1.z.boolean().default(true)
|
|
159
|
+
});
|
|
160
|
+
const DeleteStackSchema = zod_1.z.object({
|
|
161
|
+
stack_id: zod_1.z.string().describe('Stack ID'),
|
|
162
|
+
confirm: zod_1.z
|
|
163
|
+
.boolean()
|
|
164
|
+
.optional()
|
|
165
|
+
.default(false)
|
|
166
|
+
.describe('Must be true to delete'),
|
|
167
|
+
preview: zod_1.z.boolean().default(true)
|
|
168
|
+
});
|
|
169
|
+
const CreateDatabaseSchema = zod_1.z.object({
|
|
170
|
+
account_id: zod_1.z
|
|
171
|
+
.string()
|
|
172
|
+
.optional()
|
|
173
|
+
.describe('Account ID. If omitted, Scaly will infer from your accessible stacks.'),
|
|
174
|
+
name: zod_1.z.string().min(1).describe('Database add-on name'),
|
|
175
|
+
engine: zod_1.z
|
|
176
|
+
.enum(['POSTGRES_14', 'POSTGRES_16', 'MYSQL_80'])
|
|
177
|
+
.default('POSTGRES_16'),
|
|
178
|
+
size: zod_1.z
|
|
179
|
+
.enum(['Micro', 'Small', 'Medium', 'Large', 'XLarge'])
|
|
180
|
+
.default('Small'),
|
|
181
|
+
preview: zod_1.z.boolean().default(true)
|
|
182
|
+
});
|
|
183
|
+
const CreateStorageSchema = zod_1.z.object({
|
|
184
|
+
account_id: zod_1.z
|
|
185
|
+
.string()
|
|
186
|
+
.optional()
|
|
187
|
+
.describe('Account ID. If omitted, Scaly will infer from your accessible stacks.'),
|
|
188
|
+
name: zod_1.z.string().min(1).describe('Storage add-on name'),
|
|
189
|
+
preview: zod_1.z.boolean().default(true)
|
|
190
|
+
});
|
|
191
|
+
const PasswordPolicySchema = zod_1.z
|
|
192
|
+
.object({
|
|
193
|
+
minimum_length: zod_1.z.number().int().min(6).optional(),
|
|
194
|
+
require_lowercase: zod_1.z.boolean().optional(),
|
|
195
|
+
require_uppercase: zod_1.z.boolean().optional(),
|
|
196
|
+
require_numbers: zod_1.z.boolean().optional(),
|
|
197
|
+
require_symbols: zod_1.z.boolean().optional(),
|
|
198
|
+
temporary_password_validity_days: zod_1.z.number().int().min(1).optional(),
|
|
199
|
+
// Accept doc-style keys too (mapped internally).
|
|
200
|
+
minLength: zod_1.z.number().int().min(6).optional(),
|
|
201
|
+
requireLowercase: zod_1.z.boolean().optional(),
|
|
202
|
+
requireUppercase: zod_1.z.boolean().optional(),
|
|
203
|
+
requireNumbers: zod_1.z.boolean().optional(),
|
|
204
|
+
requireSymbols: zod_1.z.boolean().optional(),
|
|
205
|
+
temporaryPasswordValidityDays: zod_1.z.number().int().min(1).optional()
|
|
206
|
+
})
|
|
207
|
+
.optional();
|
|
208
|
+
const CreateUserPoolSchema = zod_1.z.object({
|
|
209
|
+
account_id: zod_1.z
|
|
210
|
+
.string()
|
|
211
|
+
.optional()
|
|
212
|
+
.describe('Account ID. If omitted, Scaly will infer from your accessible stacks.'),
|
|
213
|
+
name: zod_1.z.string().min(1).describe('Cognito user pool add-on name'),
|
|
214
|
+
password_policy: PasswordPolicySchema,
|
|
215
|
+
mfa: zod_1.z.enum(['off', 'optional', 'required']).optional().default('optional'),
|
|
216
|
+
preview: zod_1.z.boolean().default(true)
|
|
217
|
+
});
|
|
218
|
+
const CreateAppSchema = zod_1.z.object({
|
|
219
|
+
account_id: zod_1.z
|
|
220
|
+
.string()
|
|
221
|
+
.optional()
|
|
222
|
+
.describe('Account ID. If omitted, Scaly will infer from the stack.'),
|
|
223
|
+
stack_id: zod_1.z.string().describe('Stack ID'),
|
|
224
|
+
name: zod_1.z.string().min(1).describe('App name'),
|
|
225
|
+
framework: zod_1.z
|
|
226
|
+
.string()
|
|
227
|
+
.optional()
|
|
228
|
+
.describe('Optional framework label (used for guidance; not enforced by API)'),
|
|
229
|
+
min_idle: zod_1.z.number().int().min(0).optional(),
|
|
230
|
+
preview: zod_1.z.boolean().default(true)
|
|
231
|
+
});
|
|
232
|
+
const LinkAddOnSchema = zod_1.z.object({
|
|
233
|
+
app_id: zod_1.z.string().describe('App ID'),
|
|
234
|
+
addon_id: zod_1.z.string().describe('Add-on ID'),
|
|
235
|
+
preview: zod_1.z.boolean().default(true)
|
|
236
|
+
});
|
|
237
|
+
const ConfigureAuthSchema = zod_1.z.object({
|
|
238
|
+
app_id: zod_1.z.string().describe('App ID'),
|
|
239
|
+
user_pool_id: zod_1.z.string().describe('Cognito add-on ID to use for auth'),
|
|
240
|
+
groups: zod_1.z
|
|
241
|
+
.array(zod_1.z.string())
|
|
242
|
+
.optional()
|
|
243
|
+
.default([])
|
|
244
|
+
.describe('Optional list of allowed groups (empty means no group restriction)'),
|
|
245
|
+
preview: zod_1.z.boolean().default(true)
|
|
246
|
+
});
|
|
247
|
+
function normalizePasswordPolicy(input) {
|
|
248
|
+
if (!input)
|
|
249
|
+
return undefined;
|
|
250
|
+
return {
|
|
251
|
+
minimumLength: input.minimum_length ?? input.minLength,
|
|
252
|
+
requireLowercase: input.require_lowercase ?? input.requireLowercase,
|
|
253
|
+
requireUppercase: input.require_uppercase ?? input.requireUppercase,
|
|
254
|
+
requireNumbers: input.require_numbers ?? input.requireNumbers,
|
|
255
|
+
requireSymbols: input.require_symbols ?? input.requireSymbols,
|
|
256
|
+
temporaryPasswordValidityDays: input.temporary_password_validity_days ??
|
|
257
|
+
input.temporaryPasswordValidityDays
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function mfaToSettings(mfa) {
|
|
261
|
+
if (mfa === 'off')
|
|
262
|
+
return { enabled: false, required: false };
|
|
263
|
+
if (mfa === 'optional')
|
|
264
|
+
return { enabled: true, required: false };
|
|
265
|
+
return { enabled: true, required: true };
|
|
266
|
+
}
|
|
267
|
+
exports.writeTools = [
|
|
268
|
+
{
|
|
269
|
+
name: 'scaly_create_stack',
|
|
270
|
+
description: 'Create a new stack. Use preview=true first; execute with preview=false.',
|
|
271
|
+
inputSchema: CreateStackSchema,
|
|
272
|
+
handler: async (input) => {
|
|
273
|
+
try {
|
|
274
|
+
const accountId = await resolveAccountId(input.account_id);
|
|
275
|
+
if (!accountId) {
|
|
276
|
+
return (0, index_js_1.fail)({
|
|
277
|
+
code: 'VALIDATION_ERROR',
|
|
278
|
+
message: 'account_id is required (unable to infer account from accessible stacks).',
|
|
279
|
+
retriable: false
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
const existing = await stackExistsByName(accountId, input.name);
|
|
283
|
+
if (existing.length) {
|
|
284
|
+
return (0, index_js_1.fail)({
|
|
285
|
+
code: 'RESOURCE_EXISTS',
|
|
286
|
+
message: `Stack already exists: ${input.name}`,
|
|
287
|
+
retriable: false,
|
|
288
|
+
details: { matches: existing }
|
|
289
|
+
}, { existing: existing[0] });
|
|
290
|
+
}
|
|
291
|
+
if (input.preview) {
|
|
292
|
+
return (0, index_js_1.ok)({
|
|
293
|
+
preview: true,
|
|
294
|
+
drift_warning: true,
|
|
295
|
+
would_create: {
|
|
296
|
+
type: 'stack',
|
|
297
|
+
name: input.name,
|
|
298
|
+
size: input.size,
|
|
299
|
+
min_idle: input.min_idle ?? 0,
|
|
300
|
+
account_id: accountId
|
|
301
|
+
},
|
|
302
|
+
estimated_cost: {
|
|
303
|
+
monthly_usd: estimateStackMonthlyUsd(input.size)
|
|
304
|
+
},
|
|
305
|
+
estimated_duration_seconds: 30,
|
|
306
|
+
notes: input.region
|
|
307
|
+
? [
|
|
308
|
+
'region is derived from account today; region is informational only'
|
|
309
|
+
]
|
|
310
|
+
: []
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
const gql = `
|
|
314
|
+
mutation CreateStack($data: StackCreateInput!) {
|
|
315
|
+
createStack(data: $data) { id name accountId size minIdle status }
|
|
316
|
+
}
|
|
317
|
+
`;
|
|
318
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
319
|
+
data: {
|
|
320
|
+
name: input.name,
|
|
321
|
+
size: input.size,
|
|
322
|
+
minIdle: input.min_idle ?? 0,
|
|
323
|
+
account: { connect: { id: accountId } }
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
return (0, index_js_1.ok)({
|
|
327
|
+
preview: false,
|
|
328
|
+
drift_warning: true,
|
|
329
|
+
stack: result.createStack
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
return (0, index_js_1.fail)({
|
|
334
|
+
code: 'INTERNAL_ERROR',
|
|
335
|
+
message: err.message,
|
|
336
|
+
retriable: true
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'scaly_update_stack',
|
|
343
|
+
description: 'Update stack settings (size/min_idle). Use preview=true first; execute with preview=false.',
|
|
344
|
+
inputSchema: UpdateStackSchema,
|
|
345
|
+
handler: async (input) => {
|
|
346
|
+
try {
|
|
347
|
+
const existing = await getStackById(input.stack_id);
|
|
348
|
+
if (!existing) {
|
|
349
|
+
return (0, index_js_1.fail)({
|
|
350
|
+
code: 'RESOURCE_NOT_FOUND',
|
|
351
|
+
message: `Stack not found: ${input.stack_id}`,
|
|
352
|
+
retriable: false
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
if (input.preview) {
|
|
356
|
+
return (0, index_js_1.ok)({
|
|
357
|
+
preview: true,
|
|
358
|
+
drift_warning: true,
|
|
359
|
+
would_update: {
|
|
360
|
+
stack_id: input.stack_id,
|
|
361
|
+
stack_name: existing.name,
|
|
362
|
+
changes: {
|
|
363
|
+
size: input.size,
|
|
364
|
+
min_idle: input.min_idle
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
const gql = `
|
|
370
|
+
mutation UpdateStack($where: StackWhereUniqueInput!, $data: StackUpdateInput) {
|
|
371
|
+
updateStack(where: $where, data: $data) { id name accountId size minIdle status }
|
|
372
|
+
}
|
|
373
|
+
`;
|
|
374
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
375
|
+
where: { id: input.stack_id },
|
|
376
|
+
data: {
|
|
377
|
+
size: input.size,
|
|
378
|
+
minIdle: typeof input.min_idle === 'number' ? input.min_idle : undefined
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
return (0, index_js_1.ok)({
|
|
382
|
+
preview: false,
|
|
383
|
+
drift_warning: true,
|
|
384
|
+
stack: result.updateStack
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
catch (err) {
|
|
388
|
+
return (0, index_js_1.fail)({
|
|
389
|
+
code: 'INTERNAL_ERROR',
|
|
390
|
+
message: err.message,
|
|
391
|
+
retriable: true
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: 'scaly_delete_stack',
|
|
398
|
+
description: 'Delete a stack. Use preview=true first. When ready, set preview=false and confirm=true.',
|
|
399
|
+
inputSchema: DeleteStackSchema,
|
|
400
|
+
handler: async (input) => {
|
|
401
|
+
try {
|
|
402
|
+
const existing = await getStackById(input.stack_id);
|
|
403
|
+
if (!existing) {
|
|
404
|
+
return (0, index_js_1.fail)({
|
|
405
|
+
code: 'RESOURCE_NOT_FOUND',
|
|
406
|
+
message: `Stack not found: ${input.stack_id}`,
|
|
407
|
+
retriable: false
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
if (input.preview) {
|
|
411
|
+
return (0, index_js_1.ok)({
|
|
412
|
+
preview: true,
|
|
413
|
+
drift_warning: true,
|
|
414
|
+
would_delete: {
|
|
415
|
+
type: 'stack',
|
|
416
|
+
stack_id: input.stack_id,
|
|
417
|
+
stack_name: existing.name
|
|
418
|
+
},
|
|
419
|
+
warning: 'Deleting a stack is destructive. Ensure apps are removed or migrated first.'
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
if (!input.confirm) {
|
|
423
|
+
return (0, index_js_1.fail)({
|
|
424
|
+
code: 'VALIDATION_ERROR',
|
|
425
|
+
message: 'confirm must be true to delete a stack.',
|
|
426
|
+
retriable: false
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
const gql = `
|
|
430
|
+
mutation DeleteStack($where: StackWhereUniqueInput!) {
|
|
431
|
+
deleteStack(where: $where) { id name }
|
|
432
|
+
}
|
|
433
|
+
`;
|
|
434
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
435
|
+
where: { id: input.stack_id }
|
|
436
|
+
});
|
|
437
|
+
return (0, index_js_1.ok)({
|
|
438
|
+
preview: false,
|
|
439
|
+
drift_warning: true,
|
|
440
|
+
deleted: result.deleteStack
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
catch (err) {
|
|
444
|
+
return (0, index_js_1.fail)({
|
|
445
|
+
code: 'INTERNAL_ERROR',
|
|
446
|
+
message: err.message,
|
|
447
|
+
retriable: true
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: 'scaly_create_database',
|
|
454
|
+
description: 'Create a managed database add-on (Postgres/MySQL). Use preview=true first; execute with preview=false.',
|
|
455
|
+
inputSchema: CreateDatabaseSchema,
|
|
456
|
+
handler: async (input) => {
|
|
457
|
+
try {
|
|
458
|
+
const accountId = await resolveAccountId(input.account_id);
|
|
459
|
+
if (!accountId) {
|
|
460
|
+
return (0, index_js_1.fail)({
|
|
461
|
+
code: 'VALIDATION_ERROR',
|
|
462
|
+
message: 'account_id is required (unable to infer account from accessible stacks).',
|
|
463
|
+
retriable: false
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
const existing = await addOnExistsByName(accountId, input.name);
|
|
467
|
+
if (existing.length) {
|
|
468
|
+
return (0, index_js_1.fail)({
|
|
469
|
+
code: 'RESOURCE_EXISTS',
|
|
470
|
+
message: `Add-on already exists: ${input.name}`,
|
|
471
|
+
retriable: false,
|
|
472
|
+
details: { matches: existing }
|
|
473
|
+
}, { existing: existing[0] });
|
|
474
|
+
}
|
|
475
|
+
if (input.preview) {
|
|
476
|
+
return (0, index_js_1.ok)({
|
|
477
|
+
preview: true,
|
|
478
|
+
drift_warning: true,
|
|
479
|
+
would_create: {
|
|
480
|
+
type: 'database',
|
|
481
|
+
name: input.name,
|
|
482
|
+
engine: input.engine,
|
|
483
|
+
size: input.size,
|
|
484
|
+
account_id: accountId
|
|
485
|
+
},
|
|
486
|
+
estimated_cost: { monthly_usd: estimateDbMonthlyUsd(input.size) },
|
|
487
|
+
estimated_duration_seconds: 150
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
const gql = `
|
|
491
|
+
mutation CreateAddOn($data: AddOnCreateInput!) {
|
|
492
|
+
createAddOn(data: $data) {
|
|
493
|
+
id
|
|
494
|
+
name
|
|
495
|
+
type
|
|
496
|
+
accountId
|
|
497
|
+
status
|
|
498
|
+
addOnDatabase { size type }
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
`;
|
|
502
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
503
|
+
data: {
|
|
504
|
+
type: 'DATABASE',
|
|
505
|
+
name: input.name,
|
|
506
|
+
account: { connect: { id: accountId } },
|
|
507
|
+
addOnDatabase: {
|
|
508
|
+
create: {
|
|
509
|
+
size: input.size,
|
|
510
|
+
type: input.engine
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
return (0, index_js_1.ok)({
|
|
516
|
+
preview: false,
|
|
517
|
+
drift_warning: true,
|
|
518
|
+
addon: result.createAddOn
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
catch (err) {
|
|
522
|
+
return (0, index_js_1.fail)({
|
|
523
|
+
code: 'INTERNAL_ERROR',
|
|
524
|
+
message: err.message,
|
|
525
|
+
retriable: true
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
name: 'scaly_create_storage',
|
|
532
|
+
description: 'Create an S3 storage add-on. Use preview=true first; execute with preview=false.',
|
|
533
|
+
inputSchema: CreateStorageSchema,
|
|
534
|
+
handler: async (input) => {
|
|
535
|
+
try {
|
|
536
|
+
const accountId = await resolveAccountId(input.account_id);
|
|
537
|
+
if (!accountId) {
|
|
538
|
+
return (0, index_js_1.fail)({
|
|
539
|
+
code: 'VALIDATION_ERROR',
|
|
540
|
+
message: 'account_id is required (unable to infer account from accessible stacks).',
|
|
541
|
+
retriable: false
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
const existing = await addOnExistsByName(accountId, input.name);
|
|
545
|
+
if (existing.length) {
|
|
546
|
+
return (0, index_js_1.fail)({
|
|
547
|
+
code: 'RESOURCE_EXISTS',
|
|
548
|
+
message: `Add-on already exists: ${input.name}`,
|
|
549
|
+
retriable: false,
|
|
550
|
+
details: { matches: existing }
|
|
551
|
+
}, { existing: existing[0] });
|
|
552
|
+
}
|
|
553
|
+
if (input.preview) {
|
|
554
|
+
return (0, index_js_1.ok)({
|
|
555
|
+
preview: true,
|
|
556
|
+
drift_warning: true,
|
|
557
|
+
would_create: {
|
|
558
|
+
type: 'storage',
|
|
559
|
+
name: input.name,
|
|
560
|
+
account_id: accountId
|
|
561
|
+
},
|
|
562
|
+
estimated_cost: { monthly_usd: 0 },
|
|
563
|
+
notes: ['Storage is billed by usage (GB-month + requests).']
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
const gql = `
|
|
567
|
+
mutation CreateAddOn($data: AddOnCreateInput!) {
|
|
568
|
+
createAddOn(data: $data) {
|
|
569
|
+
id
|
|
570
|
+
name
|
|
571
|
+
type
|
|
572
|
+
accountId
|
|
573
|
+
status
|
|
574
|
+
addOnStorageS3 { bucket }
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
`;
|
|
578
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
579
|
+
data: {
|
|
580
|
+
type: 'STORAGE_S3',
|
|
581
|
+
name: input.name,
|
|
582
|
+
account: { connect: { id: accountId } }
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
return (0, index_js_1.ok)({
|
|
586
|
+
preview: false,
|
|
587
|
+
drift_warning: true,
|
|
588
|
+
addon: result.createAddOn
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
catch (err) {
|
|
592
|
+
return (0, index_js_1.fail)({
|
|
593
|
+
code: 'INTERNAL_ERROR',
|
|
594
|
+
message: err.message,
|
|
595
|
+
retriable: true
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
name: 'scaly_create_user_pool',
|
|
602
|
+
description: 'Create a Cognito user pool add-on. Use preview=true first; execute with preview=false. ' +
|
|
603
|
+
'Note: userPoolName is derived from add-on name; password policy defaults to AWS defaults if omitted.',
|
|
604
|
+
inputSchema: CreateUserPoolSchema,
|
|
605
|
+
handler: async (input) => {
|
|
606
|
+
try {
|
|
607
|
+
const accountId = await resolveAccountId(input.account_id);
|
|
608
|
+
if (!accountId) {
|
|
609
|
+
return (0, index_js_1.fail)({
|
|
610
|
+
code: 'VALIDATION_ERROR',
|
|
611
|
+
message: 'account_id is required (unable to infer account from accessible stacks).',
|
|
612
|
+
retriable: false
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
const existing = await addOnExistsByName(accountId, input.name);
|
|
616
|
+
if (existing.length) {
|
|
617
|
+
return (0, index_js_1.fail)({
|
|
618
|
+
code: 'RESOURCE_EXISTS',
|
|
619
|
+
message: `Add-on already exists: ${input.name}`,
|
|
620
|
+
retriable: false,
|
|
621
|
+
details: { matches: existing }
|
|
622
|
+
}, { existing: existing[0] });
|
|
623
|
+
}
|
|
624
|
+
const policy = normalizePasswordPolicy(input.password_policy);
|
|
625
|
+
const mfa = mfaToSettings(input.mfa);
|
|
626
|
+
if (input.preview) {
|
|
627
|
+
return (0, index_js_1.ok)({
|
|
628
|
+
preview: true,
|
|
629
|
+
drift_warning: true,
|
|
630
|
+
would_create: {
|
|
631
|
+
type: 'cognito',
|
|
632
|
+
name: input.name,
|
|
633
|
+
account_id: accountId,
|
|
634
|
+
password_policy: policy,
|
|
635
|
+
mfa
|
|
636
|
+
},
|
|
637
|
+
estimated_cost: { monthly_usd: 0 },
|
|
638
|
+
notes: ['Cognito is billed by MAU; estimates depend on usage.']
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
const gql = `
|
|
642
|
+
mutation CreateAddOn($data: AddOnCreateInput!) {
|
|
643
|
+
createAddOn(data: $data) {
|
|
644
|
+
id
|
|
645
|
+
name
|
|
646
|
+
type
|
|
647
|
+
accountId
|
|
648
|
+
status
|
|
649
|
+
addOnCognito { userPoolName userPoolId }
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
`;
|
|
653
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
654
|
+
data: {
|
|
655
|
+
type: 'COGNITO',
|
|
656
|
+
name: input.name,
|
|
657
|
+
account: { connect: { id: accountId } },
|
|
658
|
+
addOnCognito: {
|
|
659
|
+
create: {
|
|
660
|
+
passwordPolicy: policy,
|
|
661
|
+
mfa
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
return (0, index_js_1.ok)({
|
|
667
|
+
preview: false,
|
|
668
|
+
drift_warning: true,
|
|
669
|
+
addon: result.createAddOn
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
catch (err) {
|
|
673
|
+
return (0, index_js_1.fail)({
|
|
674
|
+
code: 'INTERNAL_ERROR',
|
|
675
|
+
message: err.message,
|
|
676
|
+
retriable: true
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: 'scaly_create_app',
|
|
683
|
+
description: 'Create an app on a stack. Use preview=true first; execute with preview=false.',
|
|
684
|
+
inputSchema: CreateAppSchema,
|
|
685
|
+
handler: async (input) => {
|
|
686
|
+
try {
|
|
687
|
+
// Infer account from stack.
|
|
688
|
+
const stackRes = await (0, client_js_1.query)(`
|
|
689
|
+
query GetStack($where: StackWhereUniqueInput!) {
|
|
690
|
+
getStack(where: $where) { id name accountId }
|
|
691
|
+
}
|
|
692
|
+
`, { where: { id: input.stack_id } });
|
|
693
|
+
const stack = stackRes.getStack;
|
|
694
|
+
if (!stack) {
|
|
695
|
+
return (0, index_js_1.fail)({
|
|
696
|
+
code: 'RESOURCE_NOT_FOUND',
|
|
697
|
+
message: `Stack not found: ${input.stack_id}`,
|
|
698
|
+
retriable: false
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
const accountId = input.account_id || stack.accountId;
|
|
702
|
+
const existing = await appExistsByName(accountId, input.name);
|
|
703
|
+
if (existing.length) {
|
|
704
|
+
return (0, index_js_1.fail)({
|
|
705
|
+
code: 'RESOURCE_EXISTS',
|
|
706
|
+
message: `App already exists: ${input.name}`,
|
|
707
|
+
retriable: false,
|
|
708
|
+
details: { matches: existing }
|
|
709
|
+
}, { existing: existing[0] });
|
|
710
|
+
}
|
|
711
|
+
if (input.preview) {
|
|
712
|
+
return (0, index_js_1.ok)({
|
|
713
|
+
preview: true,
|
|
714
|
+
drift_warning: true,
|
|
715
|
+
would_create: {
|
|
716
|
+
type: 'app',
|
|
717
|
+
name: input.name,
|
|
718
|
+
framework: input.framework ?? null,
|
|
719
|
+
stack_id: input.stack_id,
|
|
720
|
+
stack_name: stack.name,
|
|
721
|
+
account_id: accountId,
|
|
722
|
+
min_idle: input.min_idle ?? 0
|
|
723
|
+
},
|
|
724
|
+
notes: input.framework
|
|
725
|
+
? [
|
|
726
|
+
'framework is used for guidance/detection; API does not enforce it'
|
|
727
|
+
]
|
|
728
|
+
: []
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
const gql = `
|
|
732
|
+
mutation CreateApp($data: AppCreateInput!) {
|
|
733
|
+
createApp(data: $data) {
|
|
734
|
+
id
|
|
735
|
+
name
|
|
736
|
+
accountId
|
|
737
|
+
stackId
|
|
738
|
+
status
|
|
739
|
+
minIdle
|
|
740
|
+
createdAt
|
|
741
|
+
updatedAt
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
`;
|
|
745
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
746
|
+
data: {
|
|
747
|
+
name: input.name,
|
|
748
|
+
minIdle: input.min_idle ?? 0,
|
|
749
|
+
account: { connect: { id: accountId } },
|
|
750
|
+
stack: { connect: { id: input.stack_id } }
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
return (0, index_js_1.ok)({
|
|
754
|
+
preview: false,
|
|
755
|
+
drift_warning: true,
|
|
756
|
+
app: result.createApp
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
catch (err) {
|
|
760
|
+
return (0, index_js_1.fail)({
|
|
761
|
+
code: 'INTERNAL_ERROR',
|
|
762
|
+
message: err.message,
|
|
763
|
+
retriable: true
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
name: 'scaly_link_addon',
|
|
770
|
+
description: 'Link an add-on to an app (connect relationship). Use preview=true first; execute with preview=false.',
|
|
771
|
+
inputSchema: LinkAddOnSchema,
|
|
772
|
+
handler: async (input) => {
|
|
773
|
+
try {
|
|
774
|
+
const app = await getAppById(input.app_id);
|
|
775
|
+
if (!app) {
|
|
776
|
+
return (0, index_js_1.fail)({
|
|
777
|
+
code: 'RESOURCE_NOT_FOUND',
|
|
778
|
+
message: `App not found: ${input.app_id}`,
|
|
779
|
+
retriable: false
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
const addon = await getAddOnById(input.addon_id);
|
|
783
|
+
if (!addon) {
|
|
784
|
+
return (0, index_js_1.fail)({
|
|
785
|
+
code: 'RESOURCE_NOT_FOUND',
|
|
786
|
+
message: `Add-on not found: ${input.addon_id}`,
|
|
787
|
+
retriable: false
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
if (input.preview) {
|
|
791
|
+
return (0, index_js_1.ok)({
|
|
792
|
+
preview: true,
|
|
793
|
+
drift_warning: true,
|
|
794
|
+
would_link: {
|
|
795
|
+
app: { id: app.id, name: app.name },
|
|
796
|
+
addon: { id: addon.id, name: addon.name, type: addon.type }
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
const gql = `
|
|
801
|
+
mutation UpdateApp($where: AppWhereUniqueInput!, $data: AppUpdateInput) {
|
|
802
|
+
updateApp(where: $where, data: $data) { id name }
|
|
803
|
+
}
|
|
804
|
+
`;
|
|
805
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
806
|
+
where: { id: input.app_id },
|
|
807
|
+
data: {
|
|
808
|
+
addOns: {
|
|
809
|
+
connect: [{ id: input.addon_id }]
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
return (0, index_js_1.ok)({
|
|
814
|
+
preview: false,
|
|
815
|
+
drift_warning: true,
|
|
816
|
+
linked: true,
|
|
817
|
+
app: result.updateApp
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
catch (err) {
|
|
821
|
+
return (0, index_js_1.fail)({
|
|
822
|
+
code: 'INTERNAL_ERROR',
|
|
823
|
+
message: err.message,
|
|
824
|
+
retriable: true
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
name: 'scaly_configure_auth',
|
|
831
|
+
description: 'Configure app auth by linking a Cognito user pool add-on to the app and setting allowed groups. Use preview=true first; execute with preview=false.',
|
|
832
|
+
inputSchema: ConfigureAuthSchema,
|
|
833
|
+
handler: async (input) => {
|
|
834
|
+
try {
|
|
835
|
+
const app = await getAppById(input.app_id);
|
|
836
|
+
if (!app) {
|
|
837
|
+
return (0, index_js_1.fail)({
|
|
838
|
+
code: 'RESOURCE_NOT_FOUND',
|
|
839
|
+
message: `App not found: ${input.app_id}`,
|
|
840
|
+
retriable: false
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
const addon = await getAddOnById(input.user_pool_id);
|
|
844
|
+
if (!addon) {
|
|
845
|
+
return (0, index_js_1.fail)({
|
|
846
|
+
code: 'RESOURCE_NOT_FOUND',
|
|
847
|
+
message: `Add-on not found: ${input.user_pool_id}`,
|
|
848
|
+
retriable: false
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
if (addon.type !== 'COGNITO') {
|
|
852
|
+
return (0, index_js_1.fail)({
|
|
853
|
+
code: 'VALIDATION_ERROR',
|
|
854
|
+
message: `Add-on ${input.user_pool_id} is type ${addon.type}; expected COGNITO.`,
|
|
855
|
+
retriable: false,
|
|
856
|
+
details: { addon }
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
if (input.preview) {
|
|
860
|
+
return (0, index_js_1.ok)({
|
|
861
|
+
preview: true,
|
|
862
|
+
drift_warning: true,
|
|
863
|
+
would_configure_auth: {
|
|
864
|
+
app: { id: app.id, name: app.name },
|
|
865
|
+
user_pool: { id: addon.id, name: addon.name, type: addon.type },
|
|
866
|
+
groups: input.groups
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
const gql = `
|
|
871
|
+
mutation UpdateApp($where: AppWhereUniqueInput!, $data: AppUpdateInput) {
|
|
872
|
+
updateApp(where: $where, data: $data) { id name }
|
|
873
|
+
}
|
|
874
|
+
`;
|
|
875
|
+
const result = await (0, client_js_1.query)(gql, {
|
|
876
|
+
where: { id: input.app_id },
|
|
877
|
+
data: {
|
|
878
|
+
addOns: { connect: [{ id: input.user_pool_id }] },
|
|
879
|
+
auth: { upsert: { groups: input.groups } }
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
return (0, index_js_1.ok)({
|
|
883
|
+
preview: false,
|
|
884
|
+
drift_warning: true,
|
|
885
|
+
configured: true,
|
|
886
|
+
app: result.updateApp,
|
|
887
|
+
groups: input.groups
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
catch (err) {
|
|
891
|
+
return (0, index_js_1.fail)({
|
|
892
|
+
code: 'INTERNAL_ERROR',
|
|
893
|
+
message: err.message,
|
|
894
|
+
retriable: true
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
];
|
|
900
|
+
//# sourceMappingURL=write.js.map
|