@pellux/goodvibes-sdk 0.25.17 → 0.25.18
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/dist/_internal/contracts/artifacts/operator-contract.json +1 -1
- package/dist/_internal/contracts/generated/foundation-metadata.d.ts +1 -1
- package/dist/_internal/contracts/generated/foundation-metadata.js +1 -1
- package/dist/_internal/contracts/generated/operator-contract.js +1 -1
- package/dist/_internal/platform/cloudflare/manager.d.ts.map +1 -1
- package/dist/_internal/platform/cloudflare/manager.js +6 -2
- package/dist/_internal/platform/cloudflare/resources.d.ts +2 -2
- package/dist/_internal/platform/cloudflare/resources.d.ts.map +1 -1
- package/dist/_internal/platform/cloudflare/resources.js +269 -63
- package/dist/_internal/platform/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Synced from packages/contracts/src/generated/foundation-metadata.ts
|
|
2
2
|
export const FOUNDATION_METADATA = {
|
|
3
3
|
"productId": "goodvibes",
|
|
4
|
-
"productVersion": "0.25.
|
|
4
|
+
"productVersion": "0.25.18",
|
|
5
5
|
"operatorMethodCount": 224,
|
|
6
6
|
"operatorEventCount": 30,
|
|
7
7
|
"peerEndpointCount": 6
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/cloudflare/manager.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAIV,6BAA6B,EAC7B,4BAA4B,EAC5B,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EAGvB,+BAA+B,EAC/B,gCAAgC,EAChC,wBAAwB,EACxB,yBAAyB,EAMzB,gCAAgC,EAChC,iCAAiC,EAEjC,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAkBpB,qBAAa,6BAA6B;IAI5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqD;IAClF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAEZ,OAAO,EAAE,6BAA6B;IAK7D,cAAc,IAAI,OAAO,CAAC,4BAA4B,CAAC;IA0C7D,iBAAiB,CAAC,KAAK,GAAE,gCAAqC,GAAG,iCAAiC;IAkB5F,sBAAsB,CAAC,KAAK,EAAE,+BAA+B,GAAG,OAAO,CAAC,gCAAgC,CAAC;IAqEzG,QAAQ,CAAC,KAAK,GAAE,uBAA4B,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAuEhF,QAAQ,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAuB3E,SAAS,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/cloudflare/manager.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAIV,6BAA6B,EAC7B,4BAA4B,EAC5B,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EAGvB,+BAA+B,EAC/B,gCAAgC,EAChC,wBAAwB,EACxB,yBAAyB,EAMzB,gCAAgC,EAChC,iCAAiC,EAEjC,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAkBpB,qBAAa,6BAA6B;IAI5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqD;IAClF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAEZ,OAAO,EAAE,6BAA6B;IAK7D,cAAc,IAAI,OAAO,CAAC,4BAA4B,CAAC;IA0C7D,iBAAiB,CAAC,KAAK,GAAE,gCAAqC,GAAG,iCAAiC;IAkB5F,sBAAsB,CAAC,KAAK,EAAE,+BAA+B,GAAG,OAAO,CAAC,gCAAgC,CAAC;IAqEzG,QAAQ,CAAC,KAAK,GAAE,uBAA4B,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAuEhF,QAAQ,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAuB3E,SAAS,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA8S9E,MAAM,CAAC,KAAK,GAAE,qBAA0B,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAe1E,OAAO,CAAC,KAAK,GAAE,sBAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA0BnF,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,iBAAiB;YAYX,eAAe;YAaf,oBAAoB;YAapB,wBAAwB;YAaxB,gBAAgB;YAahB,WAAW;IAOzB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,yBAAyB;YAQnB,WAAW;IAazB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,SAAS;CAMlB"}
|
|
@@ -285,9 +285,13 @@ export class CloudflareControlPlaneManager {
|
|
|
285
285
|
deadLetterQueueId = requireQueueId(deadLetterQueue, deadLetterQueueName);
|
|
286
286
|
queueId = requireQueueId(queue, queueName);
|
|
287
287
|
}
|
|
288
|
-
const
|
|
288
|
+
const kvName = clean(input.kvNamespaceName) || config.kvNamespaceName || DEFAULT_KV_NAMESPACE_NAME;
|
|
289
|
+
const kvId = clean(input.kvNamespaceId) || config.kvNamespaceId;
|
|
290
|
+
const kv = components.kv ? await ensureKvNamespace(resourceContext, client, accountId, kvName, kvId, persist, steps) : undefined;
|
|
289
291
|
const r2 = components.r2 ? await ensureR2Bucket(resourceContext, client, accountId, clean(input.r2BucketName) || config.r2BucketName || DEFAULT_R2_BUCKET_NAME, persist, steps) : undefined;
|
|
290
|
-
const
|
|
292
|
+
const secretsStoreName = clean(input.secretsStoreName) || config.secretsStoreName || DEFAULT_SECRETS_STORE_NAME;
|
|
293
|
+
const secretsStoreId = clean(input.secretsStoreId) || config.secretsStoreId;
|
|
294
|
+
const secretsStore = components.secretsStore ? await ensureSecretsStore(resourceContext, client, accountId, secretsStoreName, secretsStoreId, persist, steps) : undefined;
|
|
291
295
|
const tunnel = components.zeroTrustTunnel
|
|
292
296
|
? await ensureTunnel(resourceContext, client, {
|
|
293
297
|
accountId,
|
|
@@ -6,9 +6,9 @@ export interface CloudflareProvisioningContext {
|
|
|
6
6
|
readonly storeSecret: (key: string, value: string) => Promise<void>;
|
|
7
7
|
}
|
|
8
8
|
export declare function ensureQueue(client: CloudflareApiClient, accountId: string, queueName: string, steps: CloudflareProvisionStep[], stepName: string): Promise<CloudflareQueueLike>;
|
|
9
|
-
export declare function ensureKvNamespace(context: CloudflareProvisioningContext, client: CloudflareApiClient, accountId: string, namespaceName: string, persist: boolean, steps: CloudflareProvisionStep[]): Promise<CloudflareKvNamespaceLike>;
|
|
9
|
+
export declare function ensureKvNamespace(context: CloudflareProvisioningContext, client: CloudflareApiClient, accountId: string, namespaceName: string, configuredNamespaceId: string, persist: boolean, steps: CloudflareProvisionStep[]): Promise<CloudflareKvNamespaceLike>;
|
|
10
10
|
export declare function ensureR2Bucket(context: CloudflareProvisioningContext, client: CloudflareApiClient, accountId: string, bucketName: string, persist: boolean, steps: CloudflareProvisionStep[]): Promise<CloudflareR2BucketLike>;
|
|
11
|
-
export declare function ensureSecretsStore(context: CloudflareProvisioningContext, client: CloudflareApiClient, accountId: string, storeName: string, persist: boolean, steps: CloudflareProvisionStep[]): Promise<CloudflareSecretsStoreLike>;
|
|
11
|
+
export declare function ensureSecretsStore(context: CloudflareProvisioningContext, client: CloudflareApiClient, accountId: string, storeName: string, configuredStoreId: string, persist: boolean, steps: CloudflareProvisionStep[]): Promise<CloudflareSecretsStoreLike>;
|
|
12
12
|
export declare function ensureTunnel(context: CloudflareProvisioningContext, client: CloudflareApiClient, input: {
|
|
13
13
|
readonly accountId: string;
|
|
14
14
|
readonly tunnelName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/cloudflare/resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAOrD,OAAO,KAAK,EAGV,mBAAmB,EACnB,sBAAsB,EACtB,4BAA4B,EAC5B,uBAAuB,EACvB,oCAAoC,EACpC,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,0BAA0B,EAE1B,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAKpB,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,4BAA4B,CAAC;IACxD,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/E,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,mBAAmB,EAC3B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,uBAAuB,EAAE,EAChC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/cloudflare/resources.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,68 +4,114 @@ import { CloudflareControlPlaneError } from './types.js';
|
|
|
4
4
|
import { clean, collectAsync, hostnameFromUrl } from './utils.js';
|
|
5
5
|
import { GOODVIBES_CLOUDFLARE_WORKER_MODULE } from './worker-source.js';
|
|
6
6
|
export async function ensureQueue(client, accountId, queueName, steps, stepName) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const existing = await findQueueByName(client, accountId, queueName);
|
|
8
|
+
if (existing) {
|
|
9
|
+
steps.push({ name: stepName, status: 'ok', message: `Using existing Cloudflare Queue ${queueName}.`, resourceId: existing.queue_id });
|
|
10
|
+
return existing;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const created = await client.queues.create({ account_id: accountId, queue_name: queueName });
|
|
14
|
+
steps.push({ name: stepName, status: 'ok', message: `Created Cloudflare Queue ${queueName}.`, resourceId: created.queue_id });
|
|
15
|
+
return created;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const recovered = await findQueueByName(client, accountId, queueName);
|
|
19
|
+
if (recovered) {
|
|
20
|
+
steps.push({ name: stepName, status: 'ok', message: `Using existing Cloudflare Queue ${queueName} after create retry: ${summarizeError(error)}`, resourceId: recovered.queue_id });
|
|
21
|
+
return recovered;
|
|
11
22
|
}
|
|
23
|
+
throw error;
|
|
12
24
|
}
|
|
13
|
-
const created = await client.queues.create({ account_id: accountId, queue_name: queueName });
|
|
14
|
-
steps.push({ name: stepName, status: 'ok', message: `Created Cloudflare Queue ${queueName}.`, resourceId: created.queue_id });
|
|
15
|
-
return created;
|
|
16
25
|
}
|
|
17
|
-
export async function ensureKvNamespace(context, client, accountId, namespaceName, persist, steps) {
|
|
26
|
+
export async function ensureKvNamespace(context, client, accountId, namespaceName, configuredNamespaceId, persist, steps) {
|
|
18
27
|
if (!client.kv) {
|
|
19
28
|
throw new CloudflareControlPlaneError('The Cloudflare client does not expose KV namespace APIs.', 'CLOUDFLARE_KV_API_UNAVAILABLE', 500);
|
|
20
29
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
const existing = await findKvNamespace(client, accountId, namespaceName, configuredNamespaceId);
|
|
31
|
+
if (existing) {
|
|
32
|
+
persistKvNamespace(context, existing, namespaceName, persist);
|
|
33
|
+
steps.push({ name: 'kv-namespace', status: 'ok', message: `Using existing KV namespace ${namespaceName}.`, resourceId: existing.id });
|
|
34
|
+
return existing;
|
|
35
|
+
}
|
|
36
|
+
if (configuredNamespaceId) {
|
|
37
|
+
const configured = { id: configuredNamespaceId, title: namespaceName };
|
|
38
|
+
persistKvNamespace(context, configured, namespaceName, persist);
|
|
39
|
+
steps.push({ name: 'kv-namespace', status: 'ok', message: `Using configured KV namespace ${namespaceName}.`, resourceId: configuredNamespaceId });
|
|
40
|
+
return configured;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const created = await client.kv.namespaces.create({ account_id: accountId, title: namespaceName });
|
|
44
|
+
persistKvNamespace(context, created, namespaceName, persist);
|
|
45
|
+
steps.push({ name: 'kv-namespace', status: 'ok', message: `Created KV namespace ${namespaceName}.`, resourceId: created.id });
|
|
46
|
+
return created;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const recovered = await findKvNamespace(client, accountId, namespaceName, configuredNamespaceId);
|
|
50
|
+
if (recovered) {
|
|
51
|
+
persistKvNamespace(context, recovered, namespaceName, persist);
|
|
52
|
+
steps.push({ name: 'kv-namespace', status: 'ok', message: `Using existing KV namespace ${namespaceName} after create retry: ${summarizeError(error)}`, resourceId: recovered.id });
|
|
53
|
+
return recovered;
|
|
28
54
|
}
|
|
55
|
+
throw error;
|
|
29
56
|
}
|
|
30
|
-
const created = await client.kv.namespaces.create({ account_id: accountId, title: namespaceName });
|
|
31
|
-
context.setConfig('cloudflare.kvNamespaceName', namespaceName, persist);
|
|
32
|
-
if (created.id)
|
|
33
|
-
context.setConfig('cloudflare.kvNamespaceId', created.id, persist);
|
|
34
|
-
steps.push({ name: 'kv-namespace', status: 'ok', message: `Created KV namespace ${namespaceName}.`, resourceId: created.id });
|
|
35
|
-
return created;
|
|
36
57
|
}
|
|
37
58
|
export async function ensureR2Bucket(context, client, accountId, bucketName, persist, steps) {
|
|
38
59
|
if (!client.r2) {
|
|
39
60
|
throw new CloudflareControlPlaneError('The Cloudflare client does not expose R2 bucket APIs.', 'CLOUDFLARE_R2_API_UNAVAILABLE', 500);
|
|
40
61
|
}
|
|
41
|
-
const existing =
|
|
62
|
+
const existing = await findR2BucketByName(client, accountId, bucketName);
|
|
42
63
|
if (existing) {
|
|
43
64
|
context.setConfig('cloudflare.r2BucketName', bucketName, persist);
|
|
44
65
|
steps.push({ name: 'r2-bucket', status: 'ok', message: `Using existing R2 Standard bucket ${bucketName}.`, resourceId: bucketName });
|
|
45
66
|
return existing;
|
|
46
67
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
68
|
+
try {
|
|
69
|
+
const created = await client.r2.buckets.create({ account_id: accountId, name: bucketName, storageClass: 'Standard' });
|
|
70
|
+
context.setConfig('cloudflare.r2BucketName', bucketName, persist);
|
|
71
|
+
steps.push({ name: 'r2-bucket', status: 'ok', message: `Created R2 Standard bucket ${bucketName}.`, resourceId: bucketName });
|
|
72
|
+
return created.name ? created : { ...created, name: bucketName, storage_class: 'Standard' };
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const recovered = await findR2BucketByName(client, accountId, bucketName);
|
|
76
|
+
if (recovered) {
|
|
77
|
+
context.setConfig('cloudflare.r2BucketName', bucketName, persist);
|
|
78
|
+
steps.push({ name: 'r2-bucket', status: 'ok', message: `Using existing R2 bucket ${bucketName} after create retry: ${summarizeError(error)}`, resourceId: bucketName });
|
|
79
|
+
return recovered;
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
51
83
|
}
|
|
52
|
-
export async function ensureSecretsStore(context, client, accountId, storeName, persist, steps) {
|
|
84
|
+
export async function ensureSecretsStore(context, client, accountId, storeName, configuredStoreId, persist, steps) {
|
|
53
85
|
if (!client.secretsStore) {
|
|
54
86
|
throw new CloudflareControlPlaneError('The Cloudflare client does not expose Secrets Store APIs.', 'CLOUDFLARE_SECRETS_STORE_API_UNAVAILABLE', 500);
|
|
55
87
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
88
|
+
const existing = await findSecretsStore(client, accountId, storeName, configuredStoreId);
|
|
89
|
+
if (existing) {
|
|
90
|
+
persistSecretsStore(context, existing, persist);
|
|
91
|
+
steps.push({ name: 'secrets-store', status: 'ok', message: `Using existing Cloudflare Secrets Store ${existing.name}.`, resourceId: existing.id });
|
|
92
|
+
return existing;
|
|
93
|
+
}
|
|
94
|
+
if (configuredStoreId) {
|
|
95
|
+
const configured = { id: configuredStoreId, name: storeName };
|
|
96
|
+
persistSecretsStore(context, configured, persist);
|
|
97
|
+
steps.push({ name: 'secrets-store', status: 'warning', message: `Using configured Cloudflare Secrets Store id ${configuredStoreId}; it was not visible during discovery.`, resourceId: configuredStoreId });
|
|
98
|
+
return configured;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
for await (const created of client.secretsStore.stores.create({ account_id: accountId, body: [{ name: storeName }] })) {
|
|
102
|
+
persistSecretsStore(context, created, persist);
|
|
103
|
+
steps.push({ name: 'secrets-store', status: 'ok', message: `Created Cloudflare Secrets Store ${storeName}.`, resourceId: created.id });
|
|
104
|
+
return created;
|
|
62
105
|
}
|
|
63
106
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
107
|
+
catch (error) {
|
|
108
|
+
const recovered = await findRecoverableSecretsStore(client, accountId, storeName, configuredStoreId, error);
|
|
109
|
+
if (recovered) {
|
|
110
|
+
persistSecretsStore(context, recovered, persist);
|
|
111
|
+
steps.push({ name: 'secrets-store', status: recovered.name === storeName ? 'ok' : 'warning', message: `Using existing Cloudflare Secrets Store ${recovered.name} after create retry: ${summarizeError(error)}`, resourceId: recovered.id });
|
|
112
|
+
return recovered;
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
69
115
|
}
|
|
70
116
|
throw new CloudflareControlPlaneError(`Cloudflare Secrets Store '${storeName}' did not return a created store.`, 'CLOUDFLARE_SECRETS_STORE_CREATE_FAILED', 502);
|
|
71
117
|
}
|
|
@@ -86,8 +132,16 @@ export async function ensureTunnel(context, client, input) {
|
|
|
86
132
|
}
|
|
87
133
|
}
|
|
88
134
|
if (!tunnel) {
|
|
89
|
-
|
|
90
|
-
|
|
135
|
+
try {
|
|
136
|
+
tunnel = await api.create({ account_id: input.accountId, name: input.tunnelName, config_src: 'cloudflare' });
|
|
137
|
+
input.steps.push({ name: 'zero-trust-tunnel', status: 'ok', message: `Created Zero Trust Tunnel ${input.tunnelName}.`, resourceId: tunnel.id });
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
tunnel = await findTunnelByName(client, input.accountId, input.tunnelName);
|
|
141
|
+
if (!tunnel)
|
|
142
|
+
throw error;
|
|
143
|
+
input.steps.push({ name: 'zero-trust-tunnel', status: 'ok', message: `Using existing Zero Trust Tunnel ${input.tunnelName} after create retry: ${summarizeError(error)}`, resourceId: tunnel.id });
|
|
144
|
+
}
|
|
91
145
|
}
|
|
92
146
|
else {
|
|
93
147
|
input.steps.push({ name: 'zero-trust-tunnel', status: 'ok', message: `Using existing Zero Trust Tunnel ${input.tunnelName}.`, resourceId: tunnel.id });
|
|
@@ -135,8 +189,16 @@ export async function ensureAccess(context, client, input) {
|
|
|
135
189
|
}
|
|
136
190
|
}
|
|
137
191
|
if (!serviceToken) {
|
|
138
|
-
|
|
139
|
-
|
|
192
|
+
try {
|
|
193
|
+
serviceToken = await api.serviceTokens.create({ account_id: input.accountId, name: 'GoodVibes Daemon', duration: '8760h' });
|
|
194
|
+
input.steps.push({ name: 'zero-trust-access-service-token', status: 'ok', message: 'Created Zero Trust Access service token.', resourceId: serviceToken.id });
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
serviceToken = await findAccessServiceTokenByName(api, input.accountId, 'GoodVibes Daemon');
|
|
198
|
+
if (!serviceToken)
|
|
199
|
+
throw error;
|
|
200
|
+
input.steps.push({ name: 'zero-trust-access-service-token', status: 'ok', message: `Using existing Zero Trust Access service token after create retry: ${summarizeError(error)}`, resourceId: serviceToken.id });
|
|
201
|
+
}
|
|
140
202
|
}
|
|
141
203
|
else {
|
|
142
204
|
input.steps.push({ name: 'zero-trust-access-service-token', status: 'ok', message: 'Using existing Zero Trust Access service token.', resourceId: serviceToken.id });
|
|
@@ -190,9 +252,20 @@ export async function ensureAccess(context, client, input) {
|
|
|
190
252
|
},
|
|
191
253
|
],
|
|
192
254
|
};
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
255
|
+
if (app?.id) {
|
|
256
|
+
app = await api.applications.update(app.id, accessAppParams);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
try {
|
|
260
|
+
app = await api.applications.create(accessAppParams);
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
const recovered = await findAccessApplicationByDomain(api, input.accountId, input.daemonHostname);
|
|
264
|
+
if (!recovered?.id)
|
|
265
|
+
throw error;
|
|
266
|
+
app = await api.applications.update(recovered.id, accessAppParams);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
196
269
|
if (app.id)
|
|
197
270
|
context.setConfig('cloudflare.accessAppId', app.id, input.persist);
|
|
198
271
|
input.steps.push({ name: 'zero-trust-access-app', status: 'ok', message: `Configured Zero Trust Access application for ${input.daemonHostname}.`, resourceId: app.id });
|
|
@@ -292,12 +365,30 @@ export async function configureWorkerSubdomain(context, client, input) {
|
|
|
292
365
|
});
|
|
293
366
|
}
|
|
294
367
|
}
|
|
295
|
-
|
|
296
|
-
account_id: input.accountId
|
|
297
|
-
enabled
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
368
|
+
try {
|
|
369
|
+
const existing = await client.workers.scripts.subdomain.get(input.workerName, { account_id: input.accountId });
|
|
370
|
+
if (existing.enabled) {
|
|
371
|
+
input.steps.push({ name: 'worker-subdomain', status: 'ok', message: `Using existing workers.dev route for ${input.workerName}.` });
|
|
372
|
+
return accountSubdomain;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
// Older accounts may not return script-level subdomain state before it is enabled.
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
await client.workers.scripts.subdomain.create(input.workerName, {
|
|
380
|
+
account_id: input.accountId,
|
|
381
|
+
enabled: true,
|
|
382
|
+
previews_enabled: false,
|
|
383
|
+
});
|
|
384
|
+
input.steps.push({ name: 'worker-subdomain', status: 'ok', message: `Enabled workers.dev route for ${input.workerName}.` });
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
const recovered = await client.workers.scripts.subdomain.get(input.workerName, { account_id: input.accountId });
|
|
388
|
+
if (!recovered.enabled)
|
|
389
|
+
throw error;
|
|
390
|
+
input.steps.push({ name: 'worker-subdomain', status: 'ok', message: `Using existing workers.dev route for ${input.workerName} after enable retry: ${summarizeError(error)}` });
|
|
391
|
+
}
|
|
301
392
|
return accountSubdomain;
|
|
302
393
|
}
|
|
303
394
|
export async function ensureQueueConsumer(client, input) {
|
|
@@ -320,15 +411,31 @@ export async function ensureQueueConsumer(client, input) {
|
|
|
320
411
|
return updated;
|
|
321
412
|
}
|
|
322
413
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
414
|
+
try {
|
|
415
|
+
const created = await client.queues.consumers.create(input.queueId, {
|
|
416
|
+
account_id: input.accountId,
|
|
417
|
+
type: 'worker',
|
|
418
|
+
script_name: input.workerName,
|
|
419
|
+
dead_letter_queue: input.deadLetterQueueName,
|
|
420
|
+
settings,
|
|
421
|
+
});
|
|
422
|
+
input.steps.push({ name: 'queue-consumer', status: 'ok', message: `Created Queue consumer for ${input.workerName}.`, resourceId: created.consumer_id });
|
|
423
|
+
return created;
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
const recovered = await findQueueConsumer(client, input.accountId, input.queueId, input.workerName);
|
|
427
|
+
if (!recovered?.consumer_id)
|
|
428
|
+
throw error;
|
|
429
|
+
const updated = await client.queues.consumers.update(input.queueId, recovered.consumer_id, {
|
|
430
|
+
account_id: input.accountId,
|
|
431
|
+
type: 'worker',
|
|
432
|
+
script_name: input.workerName,
|
|
433
|
+
dead_letter_queue: input.deadLetterQueueName,
|
|
434
|
+
settings,
|
|
435
|
+
});
|
|
436
|
+
input.steps.push({ name: 'queue-consumer', status: 'ok', message: `Updated existing Queue consumer for ${input.workerName} after create retry: ${summarizeError(error)}`, resourceId: updated.consumer_id });
|
|
437
|
+
return updated;
|
|
438
|
+
}
|
|
332
439
|
}
|
|
333
440
|
async function ensureCnameRecord(client, zoneId, hostname, target, steps, stepName) {
|
|
334
441
|
const existing = await collectAsync(client.dns.records.list({ zone_id: zoneId, type: 'CNAME', name: { exact: hostname } }));
|
|
@@ -347,7 +454,106 @@ async function ensureCnameRecord(client, zoneId, hostname, target, steps, stepNa
|
|
|
347
454
|
steps.push({ name: stepName, status: 'ok', message: `Updated CNAME ${hostname} -> ${target}.`, resourceId: updated.id });
|
|
348
455
|
return updated;
|
|
349
456
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
457
|
+
try {
|
|
458
|
+
const created = await client.dns.records.create(params);
|
|
459
|
+
steps.push({ name: stepName, status: 'ok', message: `Created CNAME ${hostname} -> ${target}.`, resourceId: created.id });
|
|
460
|
+
return created;
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
const recovered = await findCnameRecord(client, zoneId, hostname);
|
|
464
|
+
if (!recovered?.id)
|
|
465
|
+
throw error;
|
|
466
|
+
const updated = await client.dns.records.update(recovered.id, params);
|
|
467
|
+
steps.push({ name: stepName, status: 'ok', message: `Updated existing CNAME ${hostname} -> ${target} after create retry: ${summarizeError(error)}`, resourceId: updated.id });
|
|
468
|
+
return updated;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function findQueueByName(client, accountId, queueName) {
|
|
472
|
+
for await (const queue of client.queues.list({ account_id: accountId })) {
|
|
473
|
+
if (queue.queue_name === queueName)
|
|
474
|
+
return queue;
|
|
475
|
+
}
|
|
476
|
+
return undefined;
|
|
477
|
+
}
|
|
478
|
+
async function findKvNamespace(client, accountId, namespaceName, namespaceId) {
|
|
479
|
+
for await (const namespace of client.kv.namespaces.list({ account_id: accountId })) {
|
|
480
|
+
if (namespace.title === namespaceName || (namespaceId && namespace.id === namespaceId))
|
|
481
|
+
return namespace;
|
|
482
|
+
}
|
|
483
|
+
return undefined;
|
|
484
|
+
}
|
|
485
|
+
function persistKvNamespace(context, namespace, namespaceName, persist) {
|
|
486
|
+
context.setConfig('cloudflare.kvNamespaceName', namespace.title ?? namespaceName, persist);
|
|
487
|
+
if (namespace.id)
|
|
488
|
+
context.setConfig('cloudflare.kvNamespaceId', namespace.id, persist);
|
|
489
|
+
}
|
|
490
|
+
async function findR2BucketByName(client, accountId, bucketName) {
|
|
491
|
+
const listed = (await client.r2.buckets.list({ account_id: accountId })).buckets?.find((bucket) => bucket.name === bucketName);
|
|
492
|
+
if (listed)
|
|
493
|
+
return listed;
|
|
494
|
+
if (!client.r2.buckets.get)
|
|
495
|
+
return undefined;
|
|
496
|
+
try {
|
|
497
|
+
const bucket = await client.r2.buckets.get(bucketName, { account_id: accountId });
|
|
498
|
+
return bucket.name ? bucket : { ...bucket, name: bucketName };
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
async function findSecretsStore(client, accountId, storeName, storeId) {
|
|
505
|
+
for await (const store of client.secretsStore.stores.list({ account_id: accountId })) {
|
|
506
|
+
if (store.name === storeName || (storeId && store.id === storeId))
|
|
507
|
+
return store;
|
|
508
|
+
}
|
|
509
|
+
return undefined;
|
|
510
|
+
}
|
|
511
|
+
async function findRecoverableSecretsStore(client, accountId, storeName, storeId, error) {
|
|
512
|
+
const stores = await collectAsync(client.secretsStore.stores.list({ account_id: accountId }));
|
|
513
|
+
const exact = stores.find((store) => store.name === storeName || (storeId && store.id === storeId));
|
|
514
|
+
if (exact)
|
|
515
|
+
return exact;
|
|
516
|
+
if (isMaximumStoresExceeded(error) && stores.length === 1)
|
|
517
|
+
return stores[0];
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
function persistSecretsStore(context, store, persist) {
|
|
521
|
+
context.setConfig('cloudflare.secretsStoreName', store.name, persist);
|
|
522
|
+
context.setConfig('cloudflare.secretsStoreId', store.id, persist);
|
|
523
|
+
}
|
|
524
|
+
function isMaximumStoresExceeded(error) {
|
|
525
|
+
return summarizeError(error).toLowerCase().includes('maximum_stores_exceeded');
|
|
526
|
+
}
|
|
527
|
+
async function findTunnelByName(client, accountId, tunnelName) {
|
|
528
|
+
const api = client.zeroTrust.tunnels.cloudflared;
|
|
529
|
+
for await (const tunnel of api.list({ account_id: accountId, name: tunnelName, is_deleted: false })) {
|
|
530
|
+
if (tunnel.name === tunnelName)
|
|
531
|
+
return tunnel;
|
|
532
|
+
}
|
|
533
|
+
return undefined;
|
|
534
|
+
}
|
|
535
|
+
async function findAccessServiceTokenByName(api, accountId, name) {
|
|
536
|
+
for await (const token of api.serviceTokens.list({ account_id: accountId, name })) {
|
|
537
|
+
if (token.name === name)
|
|
538
|
+
return token;
|
|
539
|
+
}
|
|
540
|
+
return undefined;
|
|
541
|
+
}
|
|
542
|
+
async function findAccessApplicationByDomain(api, accountId, domain) {
|
|
543
|
+
for await (const app of api.applications.list({ account_id: accountId, domain, exact: true })) {
|
|
544
|
+
if (app.domain === domain)
|
|
545
|
+
return app;
|
|
546
|
+
}
|
|
547
|
+
return undefined;
|
|
548
|
+
}
|
|
549
|
+
async function findQueueConsumer(client, accountId, queueId, workerName) {
|
|
550
|
+
for await (const consumer of client.queues.consumers.list(queueId, { account_id: accountId })) {
|
|
551
|
+
if (consumer.type === 'worker' && consumer.script === workerName)
|
|
552
|
+
return consumer;
|
|
553
|
+
}
|
|
554
|
+
return undefined;
|
|
555
|
+
}
|
|
556
|
+
async function findCnameRecord(client, zoneId, hostname) {
|
|
557
|
+
const existing = await collectAsync(client.dns.records.list({ zone_id: zoneId, type: 'CNAME', name: { exact: hostname } }));
|
|
558
|
+
return existing.find((record) => record.name === hostname && record.type === 'CNAME');
|
|
353
559
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
let version = '0.25.
|
|
3
|
+
let version = '0.25.18';
|
|
4
4
|
try {
|
|
5
5
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf-8'));
|
|
6
6
|
version = pkg.version ?? version;
|