@growthub/cli 0.14.9 → 0.14.11
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/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/[providerId]/callback/route.js +35 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/[providerId]/failure/route.js +35 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/[providerId]/schedule/route.js +423 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/connect/route.js +78 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/credentials/route.js +276 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/products/[productId]/resources/route.js +173 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/products/sync/route.js +347 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/sync/route.js +293 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/upstash/provider/connect/route.js +7 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/upstash/provider/sync/route.js +7 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/upstash/sync/route.js +197 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/apps/route.js +1 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/patch/preflight/route.js +38 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +3 -20
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/test-api-record/route.js +3 -20
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/workflow/publish/route.js +407 -290
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/workflows/[providerId]/route.js +209 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceAddOnsMarketplace.jsx +806 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryActionCard.jsx +141 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/CeoCockpit.jsx +15 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +42 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphCanvas.jsx +5 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +86 -20
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ScheduleCockpit.jsx +363 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/helper-commands.js +8 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +322 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +2 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/add-ons/add-ons-client.jsx +197 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/add-ons/page.jsx +23 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/settings-shell.jsx +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +734 -61
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +15 -10
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/env-status.js +2 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +29 -19
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-serverless-flow.js +8 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/schedule-cockpit-console.js +287 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/scheduler-orchestration.js +449 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/server-secrets.js +77 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/serverless-readiness.js +583 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-add-on-callback.js +63 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-add-on-scheduler.js +519 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-add-ons.js +957 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-app-readiness.js +212 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-config.js +607 -63
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-contract-compliance.js +168 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +21 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-operator-auth.js +32 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-patch-impact.js +133 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-provenance-lineage.js +214 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-stale-surfaces.js +217 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-workflow-impact.js +170 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/provider.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/qstash.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/redis.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/search.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/vector.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/scripts/scheduler-ingress-smoke.mjs +26 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +6 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/skills/governed-workspace-mutation/SKILL.md +3 -1
- package/dist/index.js +3024 -4191
- package/package.json +1 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { readWorkspaceConfig, writeWorkspaceConfig } from "@/lib/workspace-config";
|
|
3
|
+
import {
|
|
4
|
+
getUpstashProduct,
|
|
5
|
+
listUpstashProductReadiness,
|
|
6
|
+
UPSTASH_REGION_OPTIONS,
|
|
7
|
+
withUpstashProductRegistry
|
|
8
|
+
} from "@/lib/workspace-add-ons";
|
|
9
|
+
import { appendOutcomeReceipt } from "@/lib/workspace-outcome-receipts";
|
|
10
|
+
import { readEnvVar, resolveRequiredEnv } from "@/lib/server-secrets";
|
|
11
|
+
|
|
12
|
+
const PROBE_TIMEOUT_MS = 8000;
|
|
13
|
+
|
|
14
|
+
function jsonError(message, status = 400, extra = {}) {
|
|
15
|
+
return NextResponse.json({ error: message, ...extra }, { status });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function clean(value) {
|
|
19
|
+
return String(value == null ? "" : value).trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Canonical concrete-key read — same contract as readiness + schedule runtime.
|
|
23
|
+
function envValue(key) {
|
|
24
|
+
return clean(readEnvVar(key, process.env)?.value || "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function selectedQstashRegion(region) {
|
|
28
|
+
return UPSTASH_REGION_OPTIONS.find((option) => option.id === region) || UPSTASH_REGION_OPTIONS[0];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function fetchWithTimeout(url, init = {}) {
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timeout = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
34
|
+
try {
|
|
35
|
+
return await fetch(url, { ...init, signal: controller.signal, cache: "no-store" });
|
|
36
|
+
} finally {
|
|
37
|
+
clearTimeout(timeout);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function readProbeText(response) {
|
|
42
|
+
try {
|
|
43
|
+
return clean(await response.text()).slice(0, 240);
|
|
44
|
+
} catch {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function safeUrl(baseUrl, path) {
|
|
50
|
+
const base = clean(baseUrl).replace(/\/+$/, "");
|
|
51
|
+
const suffix = clean(path).startsWith("/") ? clean(path) : `/${clean(path)}`;
|
|
52
|
+
return `${base}${suffix}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function probeJsonPaths({ baseUrl, token, paths, label }) {
|
|
56
|
+
let last = null;
|
|
57
|
+
for (const path of paths) {
|
|
58
|
+
const url = safeUrl(baseUrl, path);
|
|
59
|
+
const response = await fetchWithTimeout(url, {
|
|
60
|
+
method: "GET",
|
|
61
|
+
headers: { authorization: `Bearer ${token}` },
|
|
62
|
+
});
|
|
63
|
+
const text = await readProbeText(response);
|
|
64
|
+
last = { status: response.status, path, text };
|
|
65
|
+
if (response.ok) {
|
|
66
|
+
return {
|
|
67
|
+
ok: true,
|
|
68
|
+
baseUrl,
|
|
69
|
+
testedAt: new Date().toISOString(),
|
|
70
|
+
proof: `${label} probe ${path} returned HTTP ${response.status}`,
|
|
71
|
+
summary: `${label} sync verified with a read-only REST probe (${path}).`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const details = last ? `${last.path} returned HTTP ${last.status}` : "no endpoint returned";
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
baseUrl,
|
|
79
|
+
testedAt: new Date().toISOString(),
|
|
80
|
+
proof: `${label} probe failed: ${details}`,
|
|
81
|
+
summary: `${label} REST probe failed: ${details}.`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function probeUpstashProduct(productId, region) {
|
|
86
|
+
const product = getUpstashProduct(productId);
|
|
87
|
+
if (!product) {
|
|
88
|
+
return { ok: false, status: 400, error: "unknown Upstash product" };
|
|
89
|
+
}
|
|
90
|
+
const readiness = listUpstashProductReadiness(process.env).find((item) => item.productId === product.productId);
|
|
91
|
+
const requiredEnv = resolveRequiredEnv(product.requiredEnv, process.env);
|
|
92
|
+
if (!readiness?.configured || !requiredEnv.ok) {
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
status: 422,
|
|
96
|
+
error: `${product.label} provider credentials are not connected`,
|
|
97
|
+
missingEnv: requiredEnv.missing.length ? requiredEnv.missing : (readiness?.missingEnv || product.requiredEnv),
|
|
98
|
+
resolvedEnv: requiredEnv.resolvedKeys,
|
|
99
|
+
summary: `${product.label} provider credentials are not connected. Complete provider setup, then sync again.`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const probe = product.probe || {};
|
|
104
|
+
if (!probe.baseUrlEnv || !probe.tokenEnv || !Array.isArray(probe.paths) || !probe.paths.length) {
|
|
105
|
+
return { ok: false, status: 400, error: "unsupported Upstash product probe" };
|
|
106
|
+
}
|
|
107
|
+
const regionOption = selectedQstashRegion(region);
|
|
108
|
+
const configuredUrl = envValue(probe.baseUrlEnv) || (probe.fallbackRegionBaseUrl ? regionOption.baseUrl : "");
|
|
109
|
+
const result = await probeJsonPaths({
|
|
110
|
+
baseUrl: configuredUrl,
|
|
111
|
+
token: envValue(probe.tokenEnv),
|
|
112
|
+
paths: probe.paths,
|
|
113
|
+
label: product.label,
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
...result,
|
|
117
|
+
resolvedEnv: requiredEnv.resolvedKeys,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function POST(request) {
|
|
122
|
+
let body = {};
|
|
123
|
+
try {
|
|
124
|
+
body = await request.json();
|
|
125
|
+
} catch {
|
|
126
|
+
return jsonError("invalid json body", 400);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const productId = clean(body.productId || "upstash-qstash");
|
|
130
|
+
const region = clean(body.region || "us-east-1");
|
|
131
|
+
const plan = clean(body.plan || "free");
|
|
132
|
+
const product = getUpstashProduct(productId);
|
|
133
|
+
if (!product) return jsonError("unknown Upstash product", 400, { productId });
|
|
134
|
+
|
|
135
|
+
const syncResult = await probeUpstashProduct(product.productId, region);
|
|
136
|
+
if (!syncResult.ok) {
|
|
137
|
+
await appendOutcomeReceipt({
|
|
138
|
+
kind: "workspace-add-on-sync",
|
|
139
|
+
lane: "server-authoritative",
|
|
140
|
+
outcomeStatus: "blocked",
|
|
141
|
+
actor: "workspace-marketplace",
|
|
142
|
+
objectRefs: [{ objectId: "api-registry", objectType: "api-registry", rowName: product.label }],
|
|
143
|
+
summary: syncResult.summary || syncResult.error || `${product.label} sync failed`,
|
|
144
|
+
policyVerdict: { ok: false, violationCodes: syncResult.missingEnv?.length ? ["provider_product_not_connected"] : ["provider_probe_failed"] },
|
|
145
|
+
nextActions: syncResult.missingEnv?.length
|
|
146
|
+
? [`Complete ${product.label} setup from the provider marketplace flow, then sync again.`]
|
|
147
|
+
: [`Open the ${product.label} provider console, verify the product connection, then retry sync.`]
|
|
148
|
+
});
|
|
149
|
+
return jsonError(syncResult.error || syncResult.summary || "Upstash sync failed", syncResult.status || 502, {
|
|
150
|
+
productId: product.productId,
|
|
151
|
+
missingEnv: syncResult.missingEnv || [],
|
|
152
|
+
sync: {
|
|
153
|
+
ok: false,
|
|
154
|
+
proof: syncResult.proof || "",
|
|
155
|
+
summary: syncResult.summary || "",
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const currentConfig = await readWorkspaceConfig();
|
|
161
|
+
const nextConfig = withUpstashProductRegistry(currentConfig, {
|
|
162
|
+
productId: product.productId,
|
|
163
|
+
region,
|
|
164
|
+
plan,
|
|
165
|
+
syncResult,
|
|
166
|
+
});
|
|
167
|
+
const persisted = await writeWorkspaceConfig({ dataModel: nextConfig.dataModel });
|
|
168
|
+
const { receipt } = await appendOutcomeReceipt({
|
|
169
|
+
kind: "workspace-add-on-sync",
|
|
170
|
+
lane: "server-authoritative",
|
|
171
|
+
outcomeStatus: "published",
|
|
172
|
+
actor: "workspace-marketplace",
|
|
173
|
+
objectRefs: [{ objectId: "api-registry", objectType: "api-registry", rowName: product.label }],
|
|
174
|
+
changedFields: ["dataModel.api-registry"],
|
|
175
|
+
policyVerdict: { ok: true },
|
|
176
|
+
schemaVerdict: { ok: true },
|
|
177
|
+
summary: `${product.label} installed after provider sync probe.`,
|
|
178
|
+
nextActions: product.productId === "upstash-qstash"
|
|
179
|
+
? ["Workflow Canvas can now bind QStash/Workflow from the installed product card."]
|
|
180
|
+
: ["Use this workspace add-on from the relevant data/retrieval surfaces."]
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return NextResponse.json({
|
|
184
|
+
ok: true,
|
|
185
|
+
productId: product.productId,
|
|
186
|
+
workspaceConfig: persisted,
|
|
187
|
+
sync: {
|
|
188
|
+
ok: true,
|
|
189
|
+
proof: syncResult.proof,
|
|
190
|
+
summary: syncResult.summary,
|
|
191
|
+
testedAt: syncResult.testedAt,
|
|
192
|
+
},
|
|
193
|
+
receiptId: receipt.receiptId,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export { POST };
|
|
@@ -68,7 +68,7 @@ function safeRuntime(warnings) {
|
|
|
68
68
|
|
|
69
69
|
// Mirrors the CLI probe (cli/src/commands/workspace-surface.ts) so detection
|
|
70
70
|
// lives inside the artifact too — the bridge roadmap Item 4 called for.
|
|
71
|
-
const KNOWN_APP_DIRS = ["apps/workspace"];
|
|
71
|
+
const KNOWN_APP_DIRS = ["apps/workspace", "apps/agency-portal", "apps/portal", "studio", "app", "src"];
|
|
72
72
|
|
|
73
73
|
function detectFramework(absPath) {
|
|
74
74
|
try {
|
|
@@ -35,6 +35,38 @@ import {
|
|
|
35
35
|
} from "@/lib/workspace-patch-policy";
|
|
36
36
|
import { evaluateAppScope, requireAppScope } from "@/lib/workspace-app-registry";
|
|
37
37
|
import { appendOutcomeReceipt } from "@/lib/workspace-outcome-receipts";
|
|
38
|
+
import { readWorkspaceSourceRecords } from "@/lib/workspace-config";
|
|
39
|
+
import { buildWorkspaceMetadataStore } from "@/lib/workspace-metadata-store";
|
|
40
|
+
import { buildWorkspaceMetadataGraph } from "@/lib/workspace-metadata-graph";
|
|
41
|
+
import { derivePatchImpact } from "@/lib/workspace-patch-impact";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Report the blast radius of a proposed patch BEFORE the write — the S1
|
|
45
|
+
* spine's intended preflight consumption. Builds the metadata graph from the
|
|
46
|
+
* MERGED config (what the workspace becomes if this patch lands), maps the
|
|
47
|
+
* patched dataModel objects / dashboards to their graph node ids, and runs the
|
|
48
|
+
* pure `deriveStaleSurfaces` seed path over them. Pure, additive, never
|
|
49
|
+
* throws — on any failure the preflight verdict is unaffected and `impact`
|
|
50
|
+
* is simply omitted.
|
|
51
|
+
*/
|
|
52
|
+
async function computePatchImpact(currentConfig, mergedConfig, patch) {
|
|
53
|
+
try {
|
|
54
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) return null;
|
|
55
|
+
let sourceRecords = {};
|
|
56
|
+
try { sourceRecords = (await readWorkspaceSourceRecords()) || {}; } catch { sourceRecords = {}; }
|
|
57
|
+
// Build BOTH graphs so the impact deriver can report not just added/modified
|
|
58
|
+
// (on the merged graph) but also REMOVED objects/dashboards (whose downstream
|
|
59
|
+
// lived in the current graph). One shared, unit-tested deriver does the diff.
|
|
60
|
+
const buildGraph = (cfg) => buildWorkspaceMetadataGraph(buildWorkspaceMetadataStore({ workspaceConfig: cfg || {}, workspaceSourceRecords: sourceRecords }));
|
|
61
|
+
const currentGraph = buildGraph(currentConfig);
|
|
62
|
+
const mergedGraph = buildGraph(mergedConfig);
|
|
63
|
+
const impact = derivePatchImpact(currentGraph, mergedGraph, currentConfig || {}, mergedConfig || {});
|
|
64
|
+
if (!impact.total && !impact.removed.length) return null;
|
|
65
|
+
return impact;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
38
70
|
|
|
39
71
|
async function POST(request) {
|
|
40
72
|
let patch;
|
|
@@ -66,12 +98,14 @@ async function POST(request) {
|
|
|
66
98
|
// PATCH about the merged result. Skipped when the body is not a plain
|
|
67
99
|
// object (policy already reports that).
|
|
68
100
|
let schema = { ok: true, errors: [] };
|
|
101
|
+
let mergedConfig = null;
|
|
69
102
|
if (patch && typeof patch === "object" && !Array.isArray(patch)) {
|
|
70
103
|
const sanitized = {};
|
|
71
104
|
for (const key of WORKSPACE_PATCH_ALLOWED_FIELDS) {
|
|
72
105
|
if (Object.prototype.hasOwnProperty.call(patch, key)) sanitized[key] = patch[key];
|
|
73
106
|
}
|
|
74
107
|
const merged = applyWorkspaceConfigPatch(currentConfig || {}, sanitized);
|
|
108
|
+
mergedConfig = merged;
|
|
75
109
|
try {
|
|
76
110
|
validateWorkspaceConfig({
|
|
77
111
|
dashboards: merged.dashboards,
|
|
@@ -103,6 +137,9 @@ async function POST(request) {
|
|
|
103
137
|
}
|
|
104
138
|
}
|
|
105
139
|
|
|
140
|
+
// Blast radius of this patch BEFORE the write (additive; never blocks).
|
|
141
|
+
const impact = await computePatchImpact(currentConfig, mergedConfig, patch);
|
|
142
|
+
|
|
106
143
|
const persistence = describePersistenceMode();
|
|
107
144
|
const ok = policy.ok && schema.ok && (appScopeVerdict ? appScopeVerdict.allowed : true);
|
|
108
145
|
const repairPlan = repairPlanForViolations(policy.violations);
|
|
@@ -140,6 +177,7 @@ async function POST(request) {
|
|
|
140
177
|
schema,
|
|
141
178
|
repairPlan,
|
|
142
179
|
...(appScopeVerdict ? { appScopeVerdict } : {}),
|
|
180
|
+
...(impact ? { impact } : {}),
|
|
143
181
|
...(safeNextStep ? { safeNextStep } : {}),
|
|
144
182
|
persistence: {
|
|
145
183
|
mode: persistence.mode,
|
|
@@ -88,26 +88,9 @@ import {
|
|
|
88
88
|
validateRunInputsEnvelope,
|
|
89
89
|
summarizeRunInputs
|
|
90
90
|
} from "@/lib/orchestration-run-inputs";
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
.trim()
|
|
95
|
-
.replace(/[^a-z0-9]+/gi, "_")
|
|
96
|
-
.replace(/^_+|_+$/g, "")
|
|
97
|
-
.toUpperCase();
|
|
98
|
-
return Array.from(new Set([
|
|
99
|
-
token,
|
|
100
|
-
token ? `${token}_API_KEY` : "",
|
|
101
|
-
token ? `${token}_TOKEN` : ""
|
|
102
|
-
].filter(Boolean)));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function readServerSecret(authRef) {
|
|
106
|
-
for (const key of envKeyCandidates(authRef)) {
|
|
107
|
-
if (process.env[key]) return { key, value: process.env[key] };
|
|
108
|
-
}
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
91
|
+
// Single canonical secret resolver — shared with the serverless add-on lane so
|
|
92
|
+
// both run lanes resolve stored env tokens identically (no copy-pasted logic).
|
|
93
|
+
import { readServerSecret } from "@/lib/server-secrets";
|
|
111
94
|
|
|
112
95
|
function coerceBoolean(value) {
|
|
113
96
|
if (value === true || value === false) return value;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { readWorkspaceConfig } from "@/lib/workspace-config";
|
|
3
|
+
import { readServerSecret } from "@/lib/server-secrets";
|
|
3
4
|
|
|
4
5
|
const DEFAULT_TIMEOUT_MS = 15000;
|
|
5
6
|
|
|
@@ -18,25 +19,7 @@ function buildUrl(record) {
|
|
|
18
19
|
return `${baseUrl.replace(/\/+$/, "")}/${endpoint.replace(/^\/+/, "")}`;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
const token = String(ref || "")
|
|
23
|
-
.trim()
|
|
24
|
-
.replace(/[^a-z0-9]+/gi, "_")
|
|
25
|
-
.replace(/^_+|_+$/g, "")
|
|
26
|
-
.toUpperCase();
|
|
27
|
-
return Array.from(new Set([
|
|
28
|
-
token,
|
|
29
|
-
token ? `${token}_API_KEY` : "",
|
|
30
|
-
token ? `${token}_TOKEN` : "",
|
|
31
|
-
].filter(Boolean)));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function readServerSecret(authRef) {
|
|
35
|
-
for (const key of envKeyCandidates(authRef)) {
|
|
36
|
-
if (process.env[key]) return process.env[key];
|
|
37
|
-
}
|
|
38
|
-
return "";
|
|
39
|
-
}
|
|
22
|
+
// Secret resolution uses the single canonical resolver from lib/server-secrets.js.
|
|
40
23
|
|
|
41
24
|
function findRegistryRecord(workspaceConfig, registryId) {
|
|
42
25
|
const id = String(registryId || "").trim();
|
|
@@ -86,7 +69,7 @@ async function POST(request) {
|
|
|
86
69
|
|
|
87
70
|
const method = normalizeMethod(record.method);
|
|
88
71
|
const authRef = record.authRef || record.integrationId || dataSourceRecord?.registryId;
|
|
89
|
-
const secret = readServerSecret(authRef);
|
|
72
|
+
const secret = readServerSecret(authRef)?.value || "";
|
|
90
73
|
const controller = new AbortController();
|
|
91
74
|
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
92
75
|
|