@veltrixsecops/app-sdk 3.2.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/byol/index.cjs +3802 -0
- package/dist/byol/index.cjs.map +1 -0
- package/dist/byol/index.d.cts +578 -0
- package/dist/byol/index.d.ts +578 -0
- package/dist/byol/index.js +2587 -0
- package/dist/byol/index.js.map +1 -0
- package/dist/chunk-3WK4EVQ3.js +842 -0
- package/dist/chunk-3WK4EVQ3.js.map +1 -0
- package/dist/{chunk-EOOEHZGC.js → chunk-HIL6RIDG.js} +107 -3
- package/dist/chunk-HIL6RIDG.js.map +1 -0
- package/dist/{chunk-VWFTOFTI.js → chunk-Y6XBNZGR.js} +8 -2
- package/dist/chunk-Y6XBNZGR.js.map +1 -0
- package/dist/client/index.cjs +112 -2
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +118 -6
- package/dist/client/index.d.ts +118 -6
- package/dist/client/index.js +13 -1
- package/dist/connections/index.cjs +1787 -0
- package/dist/connections/index.cjs.map +1 -0
- package/dist/connections/index.d.cts +78 -0
- package/dist/connections/index.d.ts +78 -0
- package/dist/connections/index.js +754 -0
- package/dist/connections/index.js.map +1 -0
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/opentofu/index.cjs +138 -0
- package/dist/opentofu/index.cjs.map +1 -0
- package/dist/opentofu/index.d.cts +131 -0
- package/dist/opentofu/index.d.ts +131 -0
- package/dist/opentofu/index.js +109 -0
- package/dist/opentofu/index.js.map +1 -0
- package/dist/pipeline/index.cjs +7 -0
- package/dist/pipeline/index.cjs.map +1 -1
- package/dist/pipeline/index.d.cts +9 -2
- package/dist/pipeline/index.d.ts +9 -2
- package/dist/pipeline/index.js +3 -1
- package/dist/pipeline-beeT8dje.d.cts +408 -0
- package/dist/pipeline-beeT8dje.d.ts +408 -0
- package/dist/ui/index.cjs +136 -0
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +109 -1
- package/dist/ui/index.d.ts +109 -1
- package/dist/ui/index.js +35 -677
- package/dist/ui/index.js.map +1 -1
- package/dist/{use-app-context-OQlcmq7t.d.cts → use-app-context-Byv_fam0.d.cts} +206 -4
- package/dist/{use-app-context-OQlcmq7t.d.ts → use-app-context-Byv_fam0.d.ts} +206 -4
- package/opentofu/README.md +114 -0
- package/opentofu/modules/aws/main.tf +1008 -0
- package/opentofu/modules/aws/outputs.tf +90 -0
- package/opentofu/modules/aws/variables.tf +437 -0
- package/opentofu/modules/aws/versions.tf +23 -0
- package/opentofu/modules/azure/main.tf +920 -0
- package/opentofu/modules/azure/outputs.tf +72 -0
- package/opentofu/modules/azure/variables.tf +498 -0
- package/opentofu/modules/azure/versions.tf +27 -0
- package/opentofu/modules/gcp/main.tf +645 -0
- package/opentofu/modules/gcp/outputs.tf +73 -0
- package/opentofu/modules/gcp/variables.tf +428 -0
- package/opentofu/modules/gcp/versions.tf +27 -0
- package/opentofu/modules/hetzner/main.tf +432 -0
- package/opentofu/modules/hetzner/outputs.tf +72 -0
- package/opentofu/modules/hetzner/variables.tf +423 -0
- package/opentofu/modules/hetzner/versions.tf +26 -0
- package/package.json +33 -1
- package/dist/chunk-EOOEHZGC.js.map +0 -1
- package/dist/chunk-VWFTOFTI.js.map +0 -1
- package/dist/pipeline-A-gSsPRR.d.cts +0 -189
- package/dist/pipeline-A-gSsPRR.d.ts +0 -189
package/dist/client/index.cjs
CHANGED
|
@@ -24,7 +24,9 @@ __export(client_exports, {
|
|
|
24
24
|
addInventoryItem: () => addInventoryItem,
|
|
25
25
|
authFetch: () => authFetch,
|
|
26
26
|
createCredential: () => createCredential,
|
|
27
|
+
fetchAppOnboarding: () => fetchAppOnboarding,
|
|
27
28
|
getHostRuntime: () => getHostRuntime,
|
|
29
|
+
getOnboardingStatus: () => getOnboardingStatus,
|
|
28
30
|
listConnectivityProviders: () => listConnectivityProviders,
|
|
29
31
|
listCredentials: () => listCredentials,
|
|
30
32
|
listEnvironments: () => listEnvironments,
|
|
@@ -33,6 +35,10 @@ __export(client_exports, {
|
|
|
33
35
|
removeInventoryItem: () => removeInventoryItem,
|
|
34
36
|
requireHostRuntime: () => requireHostRuntime,
|
|
35
37
|
resolveTool: () => resolveTool,
|
|
38
|
+
revokeOnboarding: () => revokeOnboarding,
|
|
39
|
+
runOperation: () => runOperation,
|
|
40
|
+
startOnboarding: () => startOnboarding,
|
|
41
|
+
testConnection: () => testConnection,
|
|
36
42
|
updateCredential: () => updateCredential,
|
|
37
43
|
updateInventoryItem: () => updateInventoryItem
|
|
38
44
|
});
|
|
@@ -58,6 +64,9 @@ function toInventoryItem(raw) {
|
|
|
58
64
|
id: String(raw.id),
|
|
59
65
|
hostname: raw.hostname ?? "",
|
|
60
66
|
port: raw.port ?? void 0,
|
|
67
|
+
webPort: raw.webPort ?? null,
|
|
68
|
+
sshUser: raw.sshUser ?? null,
|
|
69
|
+
splunkHome: raw.splunkHome ?? null,
|
|
61
70
|
type: Array.isArray(raw.type) ? raw.type : void 0,
|
|
62
71
|
domains: Array.isArray(raw.domains) ? raw.domains : [],
|
|
63
72
|
ipRanges: Array.isArray(raw.ipRanges) ? raw.ipRanges : [],
|
|
@@ -67,7 +76,7 @@ function toInventoryItem(raw) {
|
|
|
67
76
|
};
|
|
68
77
|
}
|
|
69
78
|
async function resolveTool(name) {
|
|
70
|
-
const res = await authFetch(
|
|
79
|
+
const res = await authFetch(`/api/tools?search=${encodeURIComponent(name)}&limit=100`);
|
|
71
80
|
if (!res.ok) throw await inventoryError(res);
|
|
72
81
|
const body = await res.json();
|
|
73
82
|
const tools = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
|
|
@@ -152,7 +161,11 @@ async function listEnvironments() {
|
|
|
152
161
|
if (!res.ok) throw await environmentError(res);
|
|
153
162
|
const body = await res.json();
|
|
154
163
|
const rows = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
|
|
155
|
-
return rows.map((env) => ({
|
|
164
|
+
return rows.map((env) => ({
|
|
165
|
+
id: String(env.id),
|
|
166
|
+
name: env.name ?? "",
|
|
167
|
+
environmentTypes: Array.isArray(env.environmentTypes) ? env.environmentTypes : []
|
|
168
|
+
}));
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
// src/client/credentials.ts
|
|
@@ -180,6 +193,7 @@ function toCredentialSummary(raw) {
|
|
|
180
193
|
username: raw.username ?? "",
|
|
181
194
|
type: raw.type ?? null,
|
|
182
195
|
endpoint: raw.endpoint ?? null,
|
|
196
|
+
environmentType: raw.environmentType ?? null,
|
|
183
197
|
toolId: raw.toolId ?? "",
|
|
184
198
|
hasSecret,
|
|
185
199
|
tags: Array.isArray(raw.tags) ? raw.tags.map((t) => ({ id: String(t.id), name: String(t.name) })) : []
|
|
@@ -203,6 +217,7 @@ async function createCredential(input) {
|
|
|
203
217
|
apiToken: input.apiToken,
|
|
204
218
|
type: input.type,
|
|
205
219
|
endpoint: input.endpoint,
|
|
220
|
+
environmentType: input.environmentType ?? null,
|
|
206
221
|
toolId: input.toolId,
|
|
207
222
|
tagIds: input.tagIds ?? []
|
|
208
223
|
})
|
|
@@ -219,6 +234,7 @@ async function updateCredential(id, input) {
|
|
|
219
234
|
if (input.apiToken !== void 0) body.apiToken = input.apiToken;
|
|
220
235
|
if (input.type !== void 0) body.type = input.type;
|
|
221
236
|
if (input.endpoint !== void 0) body.endpoint = input.endpoint;
|
|
237
|
+
if (input.environmentType !== void 0) body.environmentType = input.environmentType;
|
|
222
238
|
if (input.tagIds !== void 0) body.tagIds = input.tagIds;
|
|
223
239
|
const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {
|
|
224
240
|
method: "PUT",
|
|
@@ -235,6 +251,94 @@ async function removeCredential(id) {
|
|
|
235
251
|
});
|
|
236
252
|
if (!res.ok && res.status !== 204) throw await credentialError(res);
|
|
237
253
|
}
|
|
254
|
+
async function testConnection(appId, credentialId, opts = {}) {
|
|
255
|
+
const endpoint = typeof opts.endpoint === "string" ? opts.endpoint.trim() : "";
|
|
256
|
+
const res = await authFetch(
|
|
257
|
+
`/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/test`,
|
|
258
|
+
endpoint ? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ endpoint }) } : { method: "POST" }
|
|
259
|
+
);
|
|
260
|
+
if (!res.ok) {
|
|
261
|
+
const err = await credentialError(res);
|
|
262
|
+
return { ok: false, message: err.message };
|
|
263
|
+
}
|
|
264
|
+
return await res.json();
|
|
265
|
+
}
|
|
266
|
+
async function runOperation(appId, operationId, opts = {}) {
|
|
267
|
+
const res = await authFetch(
|
|
268
|
+
`/api/apps/${encodeURIComponent(appId)}/operations/${encodeURIComponent(operationId)}`,
|
|
269
|
+
{
|
|
270
|
+
method: "POST",
|
|
271
|
+
headers: { "Content-Type": "application/json" },
|
|
272
|
+
body: JSON.stringify({ credentialId: opts.credentialId, params: opts.params ?? {} })
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
if (!res.ok) {
|
|
276
|
+
const err = await credentialError(res);
|
|
277
|
+
return { ok: false, message: err.message };
|
|
278
|
+
}
|
|
279
|
+
return await res.json();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/client/onboarding.ts
|
|
283
|
+
async function onboardingError(res) {
|
|
284
|
+
const text = await res.text().catch(() => "");
|
|
285
|
+
if (text) {
|
|
286
|
+
try {
|
|
287
|
+
const body = JSON.parse(text);
|
|
288
|
+
const message = body?.error ?? body?.message;
|
|
289
|
+
if (message) return new Error(message);
|
|
290
|
+
} catch {
|
|
291
|
+
}
|
|
292
|
+
return new Error(text);
|
|
293
|
+
}
|
|
294
|
+
return new Error(`HTTP ${res.status}`);
|
|
295
|
+
}
|
|
296
|
+
async function fetchAppOnboarding(appId) {
|
|
297
|
+
try {
|
|
298
|
+
const res = await authFetch("/api/apps/enabled", { method: "GET" });
|
|
299
|
+
if (!res.ok) return null;
|
|
300
|
+
const data = await res.json();
|
|
301
|
+
const list = Array.isArray(data) ? data : data?.data ?? [];
|
|
302
|
+
const app = list.find(
|
|
303
|
+
(a) => a.appId === appId || a.id === appId
|
|
304
|
+
);
|
|
305
|
+
return app?.connection?.onboarding ?? null;
|
|
306
|
+
} catch {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async function startOnboarding(appId, input) {
|
|
311
|
+
const res = await authFetch(
|
|
312
|
+
`/api/apps/${encodeURIComponent(appId)}/connections/onboard/start`,
|
|
313
|
+
{
|
|
314
|
+
method: "POST",
|
|
315
|
+
headers: { "Content-Type": "application/json" },
|
|
316
|
+
body: JSON.stringify({
|
|
317
|
+
environmentId: input.environmentId,
|
|
318
|
+
connectionName: input.connectionName,
|
|
319
|
+
settings: input.settings ?? {}
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
if (!res.ok) throw await onboardingError(res);
|
|
324
|
+
return await res.json();
|
|
325
|
+
}
|
|
326
|
+
async function getOnboardingStatus(appId, credentialId, opts = {}) {
|
|
327
|
+
const qs = opts.verify ? "?verify=1" : "";
|
|
328
|
+
const res = await authFetch(
|
|
329
|
+
`/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/onboard/status${qs}`
|
|
330
|
+
);
|
|
331
|
+
if (!res.ok) throw await onboardingError(res);
|
|
332
|
+
return await res.json();
|
|
333
|
+
}
|
|
334
|
+
async function revokeOnboarding(appId, credentialId) {
|
|
335
|
+
const res = await authFetch(
|
|
336
|
+
`/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/onboard/revoke`,
|
|
337
|
+
{ method: "POST" }
|
|
338
|
+
);
|
|
339
|
+
if (!res.ok) throw await onboardingError(res);
|
|
340
|
+
return await res.json();
|
|
341
|
+
}
|
|
238
342
|
|
|
239
343
|
// src/client/index.ts
|
|
240
344
|
var HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
@@ -262,7 +366,9 @@ function authFetch(input, init) {
|
|
|
262
366
|
addInventoryItem,
|
|
263
367
|
authFetch,
|
|
264
368
|
createCredential,
|
|
369
|
+
fetchAppOnboarding,
|
|
265
370
|
getHostRuntime,
|
|
371
|
+
getOnboardingStatus,
|
|
266
372
|
listConnectivityProviders,
|
|
267
373
|
listCredentials,
|
|
268
374
|
listEnvironments,
|
|
@@ -271,6 +377,10 @@ function authFetch(input, init) {
|
|
|
271
377
|
removeInventoryItem,
|
|
272
378
|
requireHostRuntime,
|
|
273
379
|
resolveTool,
|
|
380
|
+
revokeOnboarding,
|
|
381
|
+
runOperation,
|
|
382
|
+
startOnboarding,
|
|
383
|
+
testConnection,
|
|
274
384
|
updateCredential,
|
|
275
385
|
updateInventoryItem
|
|
276
386
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/index.ts","../../src/client/inventory.ts","../../src/client/connectivity.ts","../../src/client/environments.ts","../../src/client/credentials.ts"],"sourcesContent":["// ========================================================================\n// Client runtime contract — how app client bundles talk to the host.\n//\n// App client code is packaged as a hermetic ESM bundle in which `react`,\n// `react-dom`, `react/jsx-runtime`, and every `@veltrixsecops/app-sdk`\n// subpath are compile-time shims that read the host-provided runtime from\n// `globalThis.__VELTRIX_APP_RUNTIME__`. The platform installs that global\n// (with ITS React instance, the shared AppContext, and an authenticated\n// fetch) before dynamically importing any app bundle, so app components\n// render inside the host React tree with working hooks and context.\n//\n// App authors: import from '@veltrixsecops/app-sdk/client' (and /hooks) —\n// never bundle your own copy of react. Use `authFetch` for calls to your\n// app's server routes (/api/apps/<app-id>/...): plain fetch() lacks the\n// platform's Authorization header and will receive 401s.\n// ========================================================================\n\nimport type { ComponentType, Context, LazyExoticComponent } from 'react'\nimport type { AppContextValue } from '../hooks/use-app-context'\nimport type { AppPermissionsApi } from '../types/platform'\n\nexport type { AppBrandingDeclaration } from '../types/manifest'\nexport type { AppPermissionsApi, PermissionEntry, PermissionCheckOptions } from '../types/platform'\n\n// Inventory — typed helpers over the platform's components API (deployment\n// targets: servers, domains, IP/CIDR ranges). Framework-free; they use the\n// `authFetch` exported below internally.\nexport {\n listInventory,\n addInventoryItem,\n updateInventoryItem,\n removeInventoryItem,\n resolveTool,\n} from './inventory'\nexport type { Tool } from './inventory'\nexport type { InventoryItem, InventoryItemInput } from '../types/platform'\n\n// Connectivity providers — reader over the customer's ZTNA providers, used to\n// populate the Access Server link picker. Framework-free; uses `authFetch`.\nexport { listConnectivityProviders } from './connectivity'\nexport type { ConnectivityProviderRef } from '../types/platform'\n\n// Environments — reader over the customer's deployment scopes, used to populate\n// the Environment picker on Connections + Access Servers. Framework-free.\nexport { listEnvironments } from './environments'\nexport type { EnvironmentRef } from '../types/pipeline'\n\n// Credentials — typed helpers over the platform's credentials API. Paired with\n// a server (component) these form a \"connection\". Secrets are write-only:\n// `listCredentials` returns redacted summaries only. Framework-free; they use\n// the `authFetch` exported below internally.\nexport {\n listCredentials,\n createCredential,\n updateCredential,\n removeCredential,\n} from './credentials'\nexport type { Credential, CredentialSummary, CredentialInput } from '../types/platform'\n\n/** Name of the global the platform installs before loading app bundles. */\nexport const HOST_RUNTIME_GLOBAL = '__VELTRIX_APP_RUNTIME__'\n\n/**\n * The runtime surface the platform exposes to app client bundles.\n * The react/reactDom/jsxRuntime members are the host's own module objects —\n * app bundles are compiled with shims that re-export them, guaranteeing a\n * single React instance per page.\n */\nexport interface VeltrixHostRuntime {\n /** The host's `react` module object. */\n react: unknown\n /** The host's `react-dom` module object. */\n reactDom: unknown\n /** The host's `react-dom/client` module object. */\n reactDomClient?: unknown\n /** The host's `react/jsx-runtime` module object. */\n jsxRuntime: unknown\n /** Shared app context — the host wraps app pages in its Provider. */\n AppContext: Context<AppContextValue | null>\n /** fetch() with the platform's Authorization header attached. */\n authFetch: (input: string, init?: RequestInit) => Promise<Response>\n /**\n * The SDK surface app bundles receive for `@veltrixsecops/app-sdk`,\n * `.../hooks`, and `.../client` imports (useAppContext, AppContext,\n * usePipelineStatus, authFetch, getHostRuntime, ...).\n */\n sdk: Record<string, unknown>\n /**\n * The platform's design-system components and hooks, host-owned so they\n * share the single host React instance. Keyed by the exact component/hook\n * names re-exported from `@veltrixsecops/app-sdk/ui` (Button, Input, Card,\n * DataTable, useToast, ...). Present only inside the platform.\n */\n ui?: Record<string, unknown>\n /**\n * Platform-scoped permission checks (RBAC/IdP hardening, Wave C4) —\n * `opts.appId` is NOT defaulted here (omit for a platform-scoped check).\n * App page code should prefer `usePermissions()`\n * (`@veltrixsecops/app-sdk/hooks`), which defaults `opts.appId` to the\n * app's own id. Optional for compatibility with a host that hasn't\n * deployed this surface yet.\n */\n permissions?: AppPermissionsApi\n}\n\n/** Read the host runtime, or null outside the platform (tests, storybook). */\nexport function getHostRuntime(): VeltrixHostRuntime | null {\n const runtime = (globalThis as Record<string, unknown>)[HOST_RUNTIME_GLOBAL]\n return (runtime as VeltrixHostRuntime) ?? null\n}\n\n/** Read the host runtime, throwing a diagnosable error when absent. */\nexport function requireHostRuntime(): VeltrixHostRuntime {\n const runtime = getHostRuntime()\n if (!runtime) {\n throw new Error(\n 'Veltrix host runtime not found — app client bundles only run inside the ' +\n `Veltrix platform (missing globalThis.${HOST_RUNTIME_GLOBAL})`,\n )\n }\n return runtime\n}\n\n/**\n * fetch() that carries the platform's Authorization header. Required for an\n * app page to call its own server routes (/api/apps/<app-id>/...), which are\n * bearer-token protected. Falls back to plain fetch outside the platform.\n */\nexport function authFetch(input: string, init?: RequestInit): Promise<Response> {\n const runtime = getHostRuntime()\n if (runtime) return runtime.authFetch(input, init)\n return fetch(input, init)\n}\n\n/** A sidebar entry contributed by the app's client entry module. */\nexport interface AppSidebarItem {\n path: string\n label: string\n icon?: string\n}\n\n/**\n * Shape of the default export of an app's `client/index.tsx`.\n * `pages` keys must match `manifest.client.pages[].component`.\n */\nexport interface AppClientModule {\n id: string\n pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>\n sidebarItems?: AppSidebarItem[]\n}\n","// ========================================================================\n// Inventory — the deployment targets an app can deploy configuration to.\n//\n// \"Inventory\" is the app-facing name for the platform's *components*: the\n// servers (hostname/port), domains, and IP/CIDR ranges a customer has\n// registered as deploy targets. These helpers are a typed, convenient\n// surface over the platform's components API (/api/components), enriched\n// with `domains` and `ipRanges`.\n//\n// Framework-free (no React) — safe to import from any client code. Every\n// call goes through the same `authFetch` the '/client' subpath exports, so\n// requests carry the platform's Authorization header. Non-2xx responses are\n// surfaced as thrown Errors carrying the platform's error text.\n// ========================================================================\n\nimport type { InventoryItem, InventoryItemInput } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's components (inventory) API. */\nconst INVENTORY_API = '/api/components'\n\n/**\n * Loosely-typed shape of a raw component as returned by the platform, before\n * it is normalized down to the {@link InventoryItem} surface.\n */\ninterface RawInventoryItem {\n id: string\n hostname?: string\n port?: string\n type?: string[]\n domains?: string[]\n ipRanges?: string[]\n tags?: Array<{ id: string; name: string }>\n connectivityProviderId?: string | null\n credentialId?: string | null\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function inventoryError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/** Normalize a raw platform component into the typed InventoryItem surface. */\nfunction toInventoryItem(raw: RawInventoryItem): InventoryItem {\n return {\n id: String(raw.id),\n hostname: raw.hostname ?? '',\n port: raw.port ?? undefined,\n type: Array.isArray(raw.type) ? raw.type : undefined,\n domains: Array.isArray(raw.domains) ? raw.domains : [],\n ipRanges: Array.isArray(raw.ipRanges) ? raw.ipRanges : [],\n tags: Array.isArray(raw.tags)\n ? raw.tags.map((tag) => ({ id: String(tag.id), name: String(tag.name) }))\n : [],\n connectivityProviderId: raw.connectivityProviderId ?? null,\n credentialId: raw.credentialId ?? null,\n }\n}\n\n/**\n * A platform Tool. Each installed app is upserted as a Tool keyed by its\n * manifest `name`, and inventory items (components) belong to a tool.\n */\nexport interface Tool {\n id: string\n name: string\n vendor?: string\n}\n\n/**\n * Resolve the platform Tool for an app by its manifest name (the platform\n * upserts `Tool.name === app name`). The tool id is required by the platform\n * when creating an inventory item, so call this once and pass the id as\n * `toolId` to {@link addInventoryItem}. Returns null when no tool matches.\n *\n * GET /api/tools (the endpoint is paginated — `{ data, pagination }` — or a\n * bare array; both are handled).\n */\nexport async function resolveTool(name: string): Promise<Tool | null> {\n const res = await authFetch('/api/tools')\n if (!res.ok) throw await inventoryError(res)\n const body = (await res.json()) as unknown\n const tools: Tool[] = Array.isArray(body)\n ? (body as Tool[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: Tool[] }).data)\n : []\n return tools.find((tool) => tool.name === name) ?? null\n}\n\n/** List the customer's inventory (deployment targets). GET /api/components */\nexport async function listInventory(): Promise<InventoryItem[]> {\n const res = await authFetch(INVENTORY_API)\n if (!res.ok) throw await inventoryError(res)\n const data = (await res.json()) as RawInventoryItem[]\n return Array.isArray(data) ? data.map(toInventoryItem) : []\n}\n\n/** Add a new inventory item (deployment target). POST /api/components */\nexport async function addInventoryItem(input: InventoryItemInput): Promise<InventoryItem> {\n const res = await authFetch(INVENTORY_API, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) throw await inventoryError(res)\n return toInventoryItem((await res.json()) as RawInventoryItem)\n}\n\n/** Update an existing inventory item. PUT /api/components/:id */\nexport async function updateInventoryItem(\n id: string,\n input: InventoryItemInput,\n): Promise<InventoryItem> {\n const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) throw await inventoryError(res)\n return toInventoryItem((await res.json()) as RawInventoryItem)\n}\n\n/** Remove an inventory item. DELETE /api/components/:id */\nexport async function removeInventoryItem(id: string): Promise<void> {\n const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n })\n // 204 No Content is the platform's success response for delete.\n if (!res.ok && res.status !== 204) throw await inventoryError(res)\n}\n","// ========================================================================\n// Connectivity providers — the customer's ZTNA providers (Tailscale,\n// WireGuard, ZeroTier, …), platform-managed under Settings → Connectivity.\n//\n// A thin reader over the platform's connectivity-providers API\n// (/api/connectivity-providers), used to populate the ZTNA link picker when\n// creating or editing an Access Server (a component). Framework-free — every\n// call goes through the same `authFetch` the '/client' subpath exports.\n// ========================================================================\n\nimport type { ConnectivityProviderRef } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's connectivity-providers API. */\nconst CONNECTIVITY_PROVIDERS_API = '/api/connectivity-providers'\n\ninterface RawConnectivityProvider {\n id: string\n name?: string\n providerType?: string\n status?: string\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function providerError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * List the customer's ZTNA connectivity providers, used to populate the Access\n * Server link picker. GET /api/connectivity-providers (the endpoint may return\n * a bare array or a paginated `{ data, ... }` shape — both are handled).\n */\nexport async function listConnectivityProviders(): Promise<ConnectivityProviderRef[]> {\n const res = await authFetch(CONNECTIVITY_PROVIDERS_API)\n if (!res.ok) throw await providerError(res)\n const body = (await res.json()) as unknown\n const providers: RawConnectivityProvider[] = Array.isArray(body)\n ? (body as RawConnectivityProvider[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: RawConnectivityProvider[] }).data)\n : []\n return providers.map((provider) => ({\n id: String(provider.id),\n name: provider.name ?? '',\n providerType: provider.providerType ?? undefined,\n status: provider.status ?? undefined,\n }))\n}\n","// ========================================================================\n// Environments — the deployment scopes that determine where a configuration is\n// pushed. Environments are the customer's platform tags (dev/staging/prod, …);\n// a Connection (credential) or Access Server (component) is tied to one by\n// carrying the environment id in its `tagIds`.\n//\n// A thin reader over the platform's environments API (/api/environments), used\n// to populate the Environment picker on the Connections and Access Servers\n// pages. Framework-free — every call goes through the same `authFetch` the\n// '/client' subpath exports.\n// ========================================================================\n\nimport type { EnvironmentRef } from '../types/pipeline'\nimport { authFetch } from './index'\n\n/** Base route for the platform's environments API. */\nconst ENVIRONMENTS_API = '/api/environments'\n\ninterface RawEnvironment {\n id: string\n name?: string\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function environmentError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * List the customer's environments (deployment scopes). GET /api/environments\n * (the endpoint may return a bare array or a paginated `{ data, ... }` shape —\n * both are handled). Use the returned id as a `tagIds` entry to tie a Connection\n * or Access Server to that environment.\n */\nexport async function listEnvironments(): Promise<EnvironmentRef[]> {\n const res = await authFetch(ENVIRONMENTS_API)\n if (!res.ok) throw await environmentError(res)\n const body = (await res.json()) as unknown\n const rows: RawEnvironment[] = Array.isArray(body)\n ? (body as RawEnvironment[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: RawEnvironment[] }).data)\n : []\n return rows.map((env) => ({ id: String(env.id), name: env.name ?? '' }))\n}\n","// ========================================================================\n// Credentials — how an app authenticates to a server (\"connection\").\n//\n// A \"connection\" pairs a server (a platform *component* — see inventory.ts)\n// with a *credential*: the account and write-only secret used to reach that\n// server. These helpers are a typed surface over the platform's credentials\n// API (POST /api/credentials, GET /api/tools/:toolId/credentials, PUT/DELETE\n// /api/credentials/:id).\n//\n// Framework-free (no React). Every call goes through the same `authFetch` the\n// '/client' subpath exports, so requests carry the platform's Authorization\n// header. Non-2xx responses are surfaced as thrown Errors carrying the\n// platform's error text.\n//\n// SECURITY: `listCredentials` returns a REDACTED {@link CredentialSummary} —\n// secret material (password / apiToken / certificate) is dropped before it\n// reaches app code, so secrets are never held in memory or logged. Only whether\n// a secret exists is surfaced (`hasSecret`). Secrets are write-only: they can be\n// set via create/update, never read back.\n// ========================================================================\n\nimport type { CredentialInput, CredentialSummary } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's credentials API. */\nconst CREDENTIALS_API = '/api/credentials'\n\n/**\n * Loosely-typed shape of a raw credential as returned by the platform, before\n * it is redacted down to the {@link CredentialSummary} surface. The secret\n * fields (`password` / `apiToken` / `certificate`) are read here only to derive\n * `hasSecret` — they are never carried into app-visible data.\n */\ninterface RawCredential {\n id: string\n name?: string\n username?: string\n type?: string | null\n endpoint?: string | null\n toolId?: string\n // The platform redacts secrets from credential responses and surfaces only\n // whether each is set via these flags. Older platforms may still send the\n // secret fields instead — both shapes are handled below.\n hasPassword?: boolean\n hasApiToken?: boolean\n hasCertificate?: boolean\n password?: string | null\n apiToken?: string | null\n certificate?: string | null\n tags?: Array<{ id: string; name: string }>\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function credentialError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * Redact a raw platform credential down to the app-visible summary, dropping\n * every secret field and surfacing only whether a secret is stored.\n */\nfunction toCredentialSummary(raw: RawCredential): CredentialSummary {\n // Prefer the redacted has* flags; fall back to the presence of the secret\n // fields themselves for older platforms that still return them.\n const hasSecret = Boolean(\n raw.hasApiToken ||\n raw.hasPassword ||\n (raw.apiToken && raw.apiToken.length > 0) ||\n (raw.password && raw.password.length > 0),\n )\n return {\n id: String(raw.id),\n name: raw.name ?? '',\n username: raw.username ?? '',\n type: raw.type ?? null,\n endpoint: raw.endpoint ?? null,\n toolId: raw.toolId ?? '',\n hasSecret,\n tags: Array.isArray(raw.tags)\n ? raw.tags.map((t) => ({ id: String(t.id), name: String(t.name) }))\n : [],\n }\n}\n\n/**\n * List the redacted credentials registered for a tool. GET\n * /api/tools/:toolId/credentials. Secrets are stripped before return — see the\n * module's SECURITY note. Returns an empty array when the tool has none.\n */\nexport async function listCredentials(toolId: string): Promise<CredentialSummary[]> {\n const res = await authFetch(`/api/tools/${encodeURIComponent(toolId)}/credentials`)\n if (!res.ok) throw await credentialError(res)\n const data = (await res.json()) as unknown\n const rows: RawCredential[] = Array.isArray(data)\n ? (data as RawCredential[])\n : Array.isArray((data as { data?: unknown })?.data)\n ? ((data as { data: RawCredential[] }).data)\n : []\n return rows.map(toCredentialSummary)\n}\n\n/**\n * Create a credential. POST /api/credentials. The platform requires `name`,\n * `username`, `password`, `toolId`, and `tagIds` — this helper defaults\n * `tagIds` to `[]` and `password` to `''` (valid for token-only auth, where the\n * secret travels in `apiToken`). Returns the new credential's id.\n */\nexport async function createCredential(input: CredentialInput): Promise<{ id: string }> {\n const res = await authFetch(CREDENTIALS_API, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name: input.name,\n username: input.username,\n password: input.password ?? '',\n apiToken: input.apiToken,\n type: input.type,\n endpoint: input.endpoint,\n toolId: input.toolId,\n tagIds: input.tagIds ?? [],\n }),\n })\n if (!res.ok) throw await credentialError(res)\n const body = (await res.json()) as { id?: string }\n return { id: String(body.id) }\n}\n\n/**\n * Update a credential. PUT /api/credentials/:id. Only the fields you pass are\n * changed; omit `password`/`apiToken` to leave the stored secret untouched.\n */\nexport async function updateCredential(\n id: string,\n input: Partial<CredentialInput>,\n): Promise<{ id: string }> {\n const body: Record<string, unknown> = {}\n if (input.name !== undefined) body.name = input.name\n if (input.username !== undefined) body.username = input.username\n if (input.password !== undefined) body.password = input.password\n if (input.apiToken !== undefined) body.apiToken = input.apiToken\n if (input.type !== undefined) body.type = input.type\n if (input.endpoint !== undefined) body.endpoint = input.endpoint\n if (input.tagIds !== undefined) body.tagIds = input.tagIds\n const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!res.ok) throw await credentialError(res)\n const result = (await res.json().catch(() => ({}))) as { id?: string }\n return { id: result.id ? String(result.id) : id }\n}\n\n/** Remove a credential. DELETE /api/credentials/:id. */\nexport async function removeCredential(id: string): Promise<void> {\n const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n })\n // 204 No Content is the platform's success response for delete.\n if (!res.ok && res.status !== 204) throw await credentialError(res)\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts","../../src/client/inventory.ts","../../src/client/connectivity.ts","../../src/client/environments.ts","../../src/client/credentials.ts","../../src/client/onboarding.ts"],"sourcesContent":["// ========================================================================\n// Client runtime contract — how app client bundles talk to the host.\n//\n// App client code is packaged as a hermetic ESM bundle in which `react`,\n// `react-dom`, `react/jsx-runtime`, and every `@veltrixsecops/app-sdk`\n// subpath are compile-time shims that read the host-provided runtime from\n// `globalThis.__VELTRIX_APP_RUNTIME__`. The platform installs that global\n// (with ITS React instance, the shared AppContext, and an authenticated\n// fetch) before dynamically importing any app bundle, so app components\n// render inside the host React tree with working hooks and context.\n//\n// App authors: import from '@veltrixsecops/app-sdk/client' (and /hooks) —\n// never bundle your own copy of react. Use `authFetch` for calls to your\n// app's server routes (/api/apps/<app-id>/...): plain fetch() lacks the\n// platform's Authorization header and will receive 401s.\n// ========================================================================\n\nimport type { ComponentType, Context, LazyExoticComponent } from 'react'\nimport type { AppContextValue } from '../hooks/use-app-context'\nimport type { AppPermissionsApi } from '../types/platform'\n\nexport type { AppBrandingDeclaration } from '../types/manifest'\nexport type { AppPermissionsApi, PermissionEntry, PermissionCheckOptions } from '../types/platform'\n\n// Inventory — typed helpers over the platform's components API (deployment\n// targets: servers, domains, IP/CIDR ranges). Framework-free; they use the\n// `authFetch` exported below internally.\nexport {\n listInventory,\n addInventoryItem,\n updateInventoryItem,\n removeInventoryItem,\n resolveTool,\n} from './inventory'\nexport type { Tool } from './inventory'\nexport type { InventoryItem, InventoryItemInput } from '../types/platform'\n\n// Connectivity providers — reader over the customer's ZTNA providers, used to\n// populate the Access Server link picker. Framework-free; uses `authFetch`.\nexport { listConnectivityProviders } from './connectivity'\nexport type { ConnectivityProviderRef } from '../types/platform'\n\n// Environments — reader over the customer's deployment scopes, used to populate\n// the Environment picker on Connections + Access Servers. Framework-free.\nexport { listEnvironments } from './environments'\nexport type { EnvironmentRef } from '../types/pipeline'\n\n// Credentials — typed helpers over the platform's credentials API. Paired with\n// a server (component) these form a \"connection\". Secrets are write-only:\n// `listCredentials` returns redacted summaries only. Framework-free; they use\n// the `authFetch` exported below internally.\nexport {\n listCredentials,\n createCredential,\n updateCredential,\n removeCredential,\n testConnection,\n runOperation,\n} from './credentials'\nexport type { Credential, CredentialSummary, CredentialInput } from '../types/platform'\nexport type { TestConnectionResult } from '../types/pipeline'\nexport type { OperationResult } from './credentials'\n\n// Connection onboarding — the one-click \"Connect …\" flow (consent + broker).\n// Framework-free; uses `authFetch`.\nexport { startOnboarding, getOnboardingStatus, revokeOnboarding, fetchAppOnboarding } from './onboarding'\nexport type {\n StartOnboardingInput,\n StartOnboardingResult,\n OnboardingStatus,\n AppOnboardingDescriptor,\n} from './onboarding'\n\n/** Name of the global the platform installs before loading app bundles. */\nexport const HOST_RUNTIME_GLOBAL = '__VELTRIX_APP_RUNTIME__'\n\n/**\n * The runtime surface the platform exposes to app client bundles.\n * The react/reactDom/jsxRuntime members are the host's own module objects —\n * app bundles are compiled with shims that re-export them, guaranteeing a\n * single React instance per page.\n */\nexport interface VeltrixHostRuntime {\n /** The host's `react` module object. */\n react: unknown\n /** The host's `react-dom` module object. */\n reactDom: unknown\n /** The host's `react-dom/client` module object. */\n reactDomClient?: unknown\n /** The host's `react/jsx-runtime` module object. */\n jsxRuntime: unknown\n /** Shared app context — the host wraps app pages in its Provider. */\n AppContext: Context<AppContextValue | null>\n /** fetch() with the platform's Authorization header attached. */\n authFetch: (input: string, init?: RequestInit) => Promise<Response>\n /**\n * The SDK surface app bundles receive for `@veltrixsecops/app-sdk`,\n * `.../hooks`, and `.../client` imports (useAppContext, AppContext,\n * usePipelineStatus, authFetch, getHostRuntime, ...).\n */\n sdk: Record<string, unknown>\n /**\n * The platform's design-system components and hooks, host-owned so they\n * share the single host React instance. Keyed by the exact component/hook\n * names re-exported from `@veltrixsecops/app-sdk/ui` (Button, Input, Card,\n * DataTable, useToast, ...). Present only inside the platform.\n */\n ui?: Record<string, unknown>\n /**\n * Platform-scoped permission checks (RBAC/IdP hardening, Wave C4) —\n * `opts.appId` is NOT defaulted here (omit for a platform-scoped check).\n * App page code should prefer `usePermissions()`\n * (`@veltrixsecops/app-sdk/hooks`), which defaults `opts.appId` to the\n * app's own id. Optional for compatibility with a host that hasn't\n * deployed this surface yet.\n */\n permissions?: AppPermissionsApi\n}\n\n/** Read the host runtime, or null outside the platform (tests, storybook). */\nexport function getHostRuntime(): VeltrixHostRuntime | null {\n const runtime = (globalThis as Record<string, unknown>)[HOST_RUNTIME_GLOBAL]\n return (runtime as VeltrixHostRuntime) ?? null\n}\n\n/** Read the host runtime, throwing a diagnosable error when absent. */\nexport function requireHostRuntime(): VeltrixHostRuntime {\n const runtime = getHostRuntime()\n if (!runtime) {\n throw new Error(\n 'Veltrix host runtime not found — app client bundles only run inside the ' +\n `Veltrix platform (missing globalThis.${HOST_RUNTIME_GLOBAL})`,\n )\n }\n return runtime\n}\n\n/**\n * fetch() that carries the platform's Authorization header. Required for an\n * app page to call its own server routes (/api/apps/<app-id>/...), which are\n * bearer-token protected. Falls back to plain fetch outside the platform.\n */\nexport function authFetch(input: string, init?: RequestInit): Promise<Response> {\n const runtime = getHostRuntime()\n if (runtime) return runtime.authFetch(input, init)\n return fetch(input, init)\n}\n\n/** A sidebar entry contributed by the app's client entry module. */\nexport interface AppSidebarItem {\n path: string\n label: string\n icon?: string\n}\n\n/**\n * Shape of the default export of an app's `client/index.tsx`.\n * `pages` keys must match `manifest.client.pages[].component`.\n */\nexport interface AppClientModule {\n id: string\n pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>\n sidebarItems?: AppSidebarItem[]\n}\n","// ========================================================================\n// Inventory — the deployment targets an app can deploy configuration to.\n//\n// \"Inventory\" is the app-facing name for the platform's *components*: the\n// servers (hostname/port), domains, and IP/CIDR ranges a customer has\n// registered as deploy targets. These helpers are a typed, convenient\n// surface over the platform's components API (/api/components), enriched\n// with `domains` and `ipRanges`.\n//\n// Framework-free (no React) — safe to import from any client code. Every\n// call goes through the same `authFetch` the '/client' subpath exports, so\n// requests carry the platform's Authorization header. Non-2xx responses are\n// surfaced as thrown Errors carrying the platform's error text.\n// ========================================================================\n\nimport type { InventoryItem, InventoryItemInput } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's components (inventory) API. */\nconst INVENTORY_API = '/api/components'\n\n/**\n * Loosely-typed shape of a raw component as returned by the platform, before\n * it is normalized down to the {@link InventoryItem} surface.\n */\ninterface RawInventoryItem {\n id: string\n hostname?: string\n port?: string\n webPort?: string | null\n sshUser?: string | null\n splunkHome?: string | null\n type?: string[]\n domains?: string[]\n ipRanges?: string[]\n tags?: Array<{ id: string; name: string }>\n connectivityProviderId?: string | null\n credentialId?: string | null\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function inventoryError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/** Normalize a raw platform component into the typed InventoryItem surface. */\nfunction toInventoryItem(raw: RawInventoryItem): InventoryItem {\n return {\n id: String(raw.id),\n hostname: raw.hostname ?? '',\n port: raw.port ?? undefined,\n webPort: raw.webPort ?? null,\n sshUser: raw.sshUser ?? null,\n splunkHome: raw.splunkHome ?? null,\n type: Array.isArray(raw.type) ? raw.type : undefined,\n domains: Array.isArray(raw.domains) ? raw.domains : [],\n ipRanges: Array.isArray(raw.ipRanges) ? raw.ipRanges : [],\n tags: Array.isArray(raw.tags)\n ? raw.tags.map((tag) => ({ id: String(tag.id), name: String(tag.name) }))\n : [],\n connectivityProviderId: raw.connectivityProviderId ?? null,\n credentialId: raw.credentialId ?? null,\n }\n}\n\n/**\n * A platform Tool. Each installed app is upserted as a Tool keyed by its\n * manifest `name`, and inventory items (components) belong to a tool.\n */\nexport interface Tool {\n id: string\n name: string\n vendor?: string\n}\n\n/**\n * Resolve the platform Tool for an app by its manifest name (the platform\n * upserts `Tool.name === app name`). The tool id is required by the platform\n * when creating an inventory item, so call this once and pass the id as\n * `toolId` to {@link addInventoryItem}. Returns null when no tool matches.\n *\n * GET /api/tools (paginated — `{ data, pagination }` — or a bare array; both are\n * handled). We MUST filter server-side by name: the list is paginated (default 20,\n * ordered by createdAt) so once a tenant has enough apps installed, a bare\n * client-side find over just the first page silently misses a real tool whose row\n * fell to a later page — surfacing a false \"no <app> tool found, install the app\n * first\" error for an app that is in fact installed. `search` is a case-insensitive\n * contains match, so we still exact-match `tool.name === name` on the result.\n */\nexport async function resolveTool(name: string): Promise<Tool | null> {\n const res = await authFetch(`/api/tools?search=${encodeURIComponent(name)}&limit=100`)\n if (!res.ok) throw await inventoryError(res)\n const body = (await res.json()) as unknown\n const tools: Tool[] = Array.isArray(body)\n ? (body as Tool[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: Tool[] }).data)\n : []\n return tools.find((tool) => tool.name === name) ?? null\n}\n\n/** List the customer's inventory (deployment targets). GET /api/components */\nexport async function listInventory(): Promise<InventoryItem[]> {\n const res = await authFetch(INVENTORY_API)\n if (!res.ok) throw await inventoryError(res)\n const data = (await res.json()) as RawInventoryItem[]\n return Array.isArray(data) ? data.map(toInventoryItem) : []\n}\n\n/** Add a new inventory item (deployment target). POST /api/components */\nexport async function addInventoryItem(input: InventoryItemInput): Promise<InventoryItem> {\n const res = await authFetch(INVENTORY_API, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) throw await inventoryError(res)\n return toInventoryItem((await res.json()) as RawInventoryItem)\n}\n\n/** Update an existing inventory item. PUT /api/components/:id */\nexport async function updateInventoryItem(\n id: string,\n input: InventoryItemInput,\n): Promise<InventoryItem> {\n const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) throw await inventoryError(res)\n return toInventoryItem((await res.json()) as RawInventoryItem)\n}\n\n/** Remove an inventory item. DELETE /api/components/:id */\nexport async function removeInventoryItem(id: string): Promise<void> {\n const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n })\n // 204 No Content is the platform's success response for delete.\n if (!res.ok && res.status !== 204) throw await inventoryError(res)\n}\n","// ========================================================================\n// Connectivity providers — the customer's ZTNA providers (Tailscale,\n// WireGuard, ZeroTier, …), platform-managed under Settings → Connectivity.\n//\n// A thin reader over the platform's connectivity-providers API\n// (/api/connectivity-providers), used to populate the ZTNA link picker when\n// creating or editing an Access Server (a component). Framework-free — every\n// call goes through the same `authFetch` the '/client' subpath exports.\n// ========================================================================\n\nimport type { ConnectivityProviderRef } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's connectivity-providers API. */\nconst CONNECTIVITY_PROVIDERS_API = '/api/connectivity-providers'\n\ninterface RawConnectivityProvider {\n id: string\n name?: string\n providerType?: string\n status?: string\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function providerError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * List the customer's ZTNA connectivity providers, used to populate the Access\n * Server link picker. GET /api/connectivity-providers (the endpoint may return\n * a bare array or a paginated `{ data, ... }` shape — both are handled).\n */\nexport async function listConnectivityProviders(): Promise<ConnectivityProviderRef[]> {\n const res = await authFetch(CONNECTIVITY_PROVIDERS_API)\n if (!res.ok) throw await providerError(res)\n const body = (await res.json()) as unknown\n const providers: RawConnectivityProvider[] = Array.isArray(body)\n ? (body as RawConnectivityProvider[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: RawConnectivityProvider[] }).data)\n : []\n return providers.map((provider) => ({\n id: String(provider.id),\n name: provider.name ?? '',\n providerType: provider.providerType ?? undefined,\n status: provider.status ?? undefined,\n }))\n}\n","// ========================================================================\n// Environments — the deployment scopes that determine where a configuration is\n// pushed. Environments are the customer's platform tags (dev/staging/prod, …);\n// a Connection (credential) or Access Server (component) is tied to one by\n// carrying the environment id in its `tagIds`.\n//\n// A thin reader over the platform's environments API (/api/environments), used\n// to populate the Environment picker on the Connections and Access Servers\n// pages. Framework-free — every call goes through the same `authFetch` the\n// '/client' subpath exports.\n// ========================================================================\n\nimport type { EnvironmentRef } from '../types/pipeline'\nimport { authFetch } from './index'\n\n/** Base route for the platform's environments API. */\nconst ENVIRONMENTS_API = '/api/environments'\n\ninterface RawEnvironment {\n id: string\n name?: string\n environmentTypes?: string[]\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function environmentError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * List the customer's environments (deployment scopes). GET /api/environments\n * (the endpoint may return a bare array or a paginated `{ data, ... }` shape —\n * both are handled). Use the returned id as a `tagIds` entry to tie a Connection\n * or Access Server to that environment.\n */\nexport async function listEnvironments(): Promise<EnvironmentRef[]> {\n const res = await authFetch(ENVIRONMENTS_API)\n if (!res.ok) throw await environmentError(res)\n const body = (await res.json()) as unknown\n const rows: RawEnvironment[] = Array.isArray(body)\n ? (body as RawEnvironment[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: RawEnvironment[] }).data)\n : []\n return rows.map((env) => ({\n id: String(env.id),\n name: env.name ?? '',\n environmentTypes: Array.isArray(env.environmentTypes) ? env.environmentTypes : [],\n }))\n}\n","// ========================================================================\n// Credentials — how an app authenticates to a server (\"connection\").\n//\n// A \"connection\" pairs a server (a platform *component* — see inventory.ts)\n// with a *credential*: the account and write-only secret used to reach that\n// server. These helpers are a typed surface over the platform's credentials\n// API (POST /api/credentials, GET /api/tools/:toolId/credentials, PUT/DELETE\n// /api/credentials/:id).\n//\n// Framework-free (no React). Every call goes through the same `authFetch` the\n// '/client' subpath exports, so requests carry the platform's Authorization\n// header. Non-2xx responses are surfaced as thrown Errors carrying the\n// platform's error text.\n//\n// SECURITY: `listCredentials` returns a REDACTED {@link CredentialSummary} —\n// secret material (password / apiToken / certificate) is dropped before it\n// reaches app code, so secrets are never held in memory or logged. Only whether\n// a secret exists is surfaced (`hasSecret`). Secrets are write-only: they can be\n// set via create/update, never read back.\n// ========================================================================\n\nimport type { CredentialInput, CredentialSummary } from '../types/platform'\nimport type { TestConnectionResult } from '../types/pipeline'\nimport { authFetch } from './index'\n\n/** Base route for the platform's credentials API. */\nconst CREDENTIALS_API = '/api/credentials'\n\n/**\n * Loosely-typed shape of a raw credential as returned by the platform, before\n * it is redacted down to the {@link CredentialSummary} surface. The secret\n * fields (`password` / `apiToken` / `certificate`) are read here only to derive\n * `hasSecret` — they are never carried into app-visible data.\n */\ninterface RawCredential {\n id: string\n name?: string\n username?: string\n type?: string | null\n endpoint?: string | null\n environmentType?: string | null\n toolId?: string\n // The platform redacts secrets from credential responses and surfaces only\n // whether each is set via these flags. Older platforms may still send the\n // secret fields instead — both shapes are handled below.\n hasPassword?: boolean\n hasApiToken?: boolean\n hasCertificate?: boolean\n password?: string | null\n apiToken?: string | null\n certificate?: string | null\n tags?: Array<{ id: string; name: string }>\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function credentialError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * Redact a raw platform credential down to the app-visible summary, dropping\n * every secret field and surfacing only whether a secret is stored.\n */\nfunction toCredentialSummary(raw: RawCredential): CredentialSummary {\n // Prefer the redacted has* flags; fall back to the presence of the secret\n // fields themselves for older platforms that still return them.\n const hasSecret = Boolean(\n raw.hasApiToken ||\n raw.hasPassword ||\n (raw.apiToken && raw.apiToken.length > 0) ||\n (raw.password && raw.password.length > 0),\n )\n return {\n id: String(raw.id),\n name: raw.name ?? '',\n username: raw.username ?? '',\n type: raw.type ?? null,\n endpoint: raw.endpoint ?? null,\n environmentType: raw.environmentType ?? null,\n toolId: raw.toolId ?? '',\n hasSecret,\n tags: Array.isArray(raw.tags)\n ? raw.tags.map((t) => ({ id: String(t.id), name: String(t.name) }))\n : [],\n }\n}\n\n/**\n * List the redacted credentials registered for a tool. GET\n * /api/tools/:toolId/credentials. Secrets are stripped before return — see the\n * module's SECURITY note. Returns an empty array when the tool has none.\n */\nexport async function listCredentials(toolId: string): Promise<CredentialSummary[]> {\n const res = await authFetch(`/api/tools/${encodeURIComponent(toolId)}/credentials`)\n if (!res.ok) throw await credentialError(res)\n const data = (await res.json()) as unknown\n const rows: RawCredential[] = Array.isArray(data)\n ? (data as RawCredential[])\n : Array.isArray((data as { data?: unknown })?.data)\n ? ((data as { data: RawCredential[] }).data)\n : []\n return rows.map(toCredentialSummary)\n}\n\n/**\n * Create a credential. POST /api/credentials. The platform requires `name`,\n * `username`, `password`, `toolId`, and `tagIds` — this helper defaults\n * `tagIds` to `[]` and `password` to `''` (valid for token-only auth, where the\n * secret travels in `apiToken`). Returns the new credential's id.\n */\nexport async function createCredential(input: CredentialInput): Promise<{ id: string }> {\n const res = await authFetch(CREDENTIALS_API, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name: input.name,\n username: input.username,\n password: input.password ?? '',\n apiToken: input.apiToken,\n type: input.type,\n endpoint: input.endpoint,\n environmentType: input.environmentType ?? null,\n toolId: input.toolId,\n tagIds: input.tagIds ?? [],\n }),\n })\n if (!res.ok) throw await credentialError(res)\n const body = (await res.json()) as { id?: string }\n return { id: String(body.id) }\n}\n\n/**\n * Update a credential. PUT /api/credentials/:id. Only the fields you pass are\n * changed; omit `password`/`apiToken` to leave the stored secret untouched.\n */\nexport async function updateCredential(\n id: string,\n input: Partial<CredentialInput>,\n): Promise<{ id: string }> {\n const body: Record<string, unknown> = {}\n if (input.name !== undefined) body.name = input.name\n if (input.username !== undefined) body.username = input.username\n if (input.password !== undefined) body.password = input.password\n if (input.apiToken !== undefined) body.apiToken = input.apiToken\n if (input.type !== undefined) body.type = input.type\n if (input.endpoint !== undefined) body.endpoint = input.endpoint\n if (input.environmentType !== undefined) body.environmentType = input.environmentType\n if (input.tagIds !== undefined) body.tagIds = input.tagIds\n const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!res.ok) throw await credentialError(res)\n const result = (await res.json().catch(() => ({}))) as { id?: string }\n return { id: result.id ? String(result.id) : id }\n}\n\n/** Remove a credential. DELETE /api/credentials/:id. */\nexport async function removeCredential(id: string): Promise<void> {\n const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n })\n // 204 No Content is the platform's success response for delete.\n if (!res.ok && res.status !== 204) throw await credentialError(res)\n}\n\nexport type { TestConnectionResult }\n\n/**\n * Test a Connection's endpoint + credential. The platform decrypts the secret,\n * runs the owning app's connectivity-test handler in-process (the secret is\n * never returned), and reports whether the endpoint + credentials actually work.\n * A failed test resolves normally with `{ ok: false, message }` — it does not\n * throw. Only transport/auth errors (401/404) surface as `{ ok: false }` too.\n *\n * @param appId the app that owns the connection, e.g. `splunk-cloud`\n * @param credentialId the connection (credential) id\n * @param opts.endpoint optional endpoint to test against instead of the one stored\n * on the credential — e.g. an Access Server's reachable address\n * (`<host>:<port>`). The host is scoped to the caller's tenant.\n */\nexport async function testConnection(\n appId: string,\n credentialId: string,\n opts: { endpoint?: string } = {},\n): Promise<TestConnectionResult> {\n const endpoint = typeof opts.endpoint === 'string' ? opts.endpoint.trim() : ''\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/test`,\n endpoint\n ? { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ endpoint }) }\n : { method: 'POST' },\n )\n if (!res.ok) {\n const err = await credentialError(res)\n return { ok: false, message: err.message }\n }\n return (await res.json()) as TestConnectionResult\n}\n\n/** Result of running an app operation. Mirrors the server operation handler's return. */\nexport interface OperationResult {\n ok: boolean\n message: string\n details?: string[]\n /** Optional structured payload (e.g. an export download URL, a job id). */\n data?: Record<string, unknown>\n}\n\n/**\n * Run an app operation — a one-off action (restart, export, retry, …), NOT a\n * configuration deploy. The platform decrypts the chosen connection's secret and\n * runs the app's operation handler in-process (the secret is never returned). A\n * failed operation resolves normally with `{ ok: false, message }` rather than\n * throwing.\n *\n * @param appId the app that owns the operation, e.g. `splunk-cloud`\n * @param operationId the operation id declared in the app manifest\n * @param opts the connection (credentialId) to authenticate with, + params\n */\nexport async function runOperation(\n appId: string,\n operationId: string,\n opts: { credentialId?: string; params?: Record<string, unknown> } = {},\n): Promise<OperationResult> {\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/operations/${encodeURIComponent(operationId)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ credentialId: opts.credentialId, params: opts.params ?? {} }),\n },\n )\n if (!res.ok) {\n const err = await credentialError(res)\n return { ok: false, message: err.message }\n }\n return (await res.json()) as OperationResult\n}\n","// ========================================================================\n// Connection onboarding — one-click \"Connect …\" flow (consent + broker).\n//\n// Typed surface over the platform's generic onboarding routes\n// (POST /api/apps/:appId/connections/onboard/start, …/onboard/status,\n// …/onboard/revoke). Framework-free; every call carries the platform's\n// Authorization header via the shared `authFetch`.\n//\n// The flow: call `startOnboarding` → open the returned `authorizeUrl` (the\n// admin consents on Microsoft's hosted page) → the platform's static callback\n// persists the connection and redirects back to the Connections page with an\n// `?onboarded=ok` flag. No secret is ever handled by app code.\n// ========================================================================\n\nimport { authFetch } from './index'\n\nasync function onboardingError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n /* not JSON */\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/** The client-safe onboarding descriptor an app advertises in /apps/enabled. */\nexport interface AppOnboardingDescriptor {\n provider: string\n label: string\n brokered?: boolean\n requiredSettings?: string[]\n}\n\n/**\n * Fetch an app's connection-onboarding descriptor from GET /api/apps/enabled,\n * so the Connections UI can offer an \"Integrate via OAuth\" option without the\n * app page having to pass it in. Returns null when the app declares no\n * onboarding (its connections stay credential-only).\n */\nexport async function fetchAppOnboarding(appId: string): Promise<AppOnboardingDescriptor | null> {\n try {\n const res = await authFetch('/api/apps/enabled', { method: 'GET' })\n if (!res.ok) return null\n const data = (await res.json()) as unknown\n const list = Array.isArray(data) ? data : ((data as { data?: unknown[] })?.data ?? [])\n const app = (list as Array<{ appId?: string; id?: string; connection?: { onboarding?: AppOnboardingDescriptor } }>).find(\n (a) => a.appId === appId || a.id === appId,\n )\n return app?.connection?.onboarding ?? null\n } catch {\n return null\n }\n}\n\nexport interface StartOnboardingInput {\n /** The deployment scope (environment) the new connection belongs to. */\n environmentId: string\n /** Display name for the new connection. */\n connectionName: string\n /**\n * Settings the admin supplies BEFORE consent (e.g. Sentinel's\n * subscription_id / resource_group / workspace_name, or a cloud override).\n */\n settings?: Record<string, unknown>\n}\n\nexport interface StartOnboardingResult {\n /** Open this in the browser — the provider's hosted consent page. */\n authorizeUrl: string\n provider: string\n}\n\n/** Begin onboarding a connection. POST /api/apps/:appId/connections/onboard/start. */\nexport async function startOnboarding(\n appId: string,\n input: StartOnboardingInput,\n): Promise<StartOnboardingResult> {\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/connections/onboard/start`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n environmentId: input.environmentId,\n connectionName: input.connectionName,\n settings: input.settings ?? {},\n }),\n },\n )\n if (!res.ok) throw await onboardingError(res)\n return (await res.json()) as StartOnboardingResult\n}\n\nexport interface OnboardingStatus {\n provider: string\n brokered: boolean\n tenantId: string | null\n cloud: string\n manualSteps: Array<{\n type: string\n title: string\n description: string\n deepLink?: string\n cliCommand?: string\n verifiable: boolean\n }>\n verify?: { effective: boolean; message: string }\n}\n\n/**\n * Read onboarding status for a connection, optionally running the verify probe\n * for pending manual steps (e.g. the Sentinel role assignment).\n * GET /api/apps/:appId/connections/:credentialId/onboard/status.\n */\nexport async function getOnboardingStatus(\n appId: string,\n credentialId: string,\n opts: { verify?: boolean } = {},\n): Promise<OnboardingStatus> {\n const qs = opts.verify ? '?verify=1' : ''\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/onboard/status${qs}`,\n )\n if (!res.ok) throw await onboardingError(res)\n return (await res.json()) as OnboardingStatus\n}\n\n/**\n * Disconnect an onboarded connection (best-effort tenant-side teardown, then\n * delete). POST /api/apps/:appId/connections/:credentialId/onboard/revoke.\n */\nexport async function revokeOnboarding(\n appId: string,\n credentialId: string,\n): Promise<{ cleaned: boolean; message: string }> {\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/onboard/revoke`,\n { method: 'POST' },\n )\n if (!res.ok) throw await onboardingError(res)\n return (await res.json()) as { cleaned: boolean; message: string }\n}\n"],"mappings":"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","names":[]}
|
package/dist/client/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ComponentType, LazyExoticComponent, Context } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
export { b as AppBrandingDeclaration,
|
|
4
|
-
import { E as EnvironmentRef } from '../pipeline-
|
|
2
|
+
import { F as InventoryItemInput, I as InventoryItem, y as ConnectivityProviderRef, B as CredentialInput, D as CredentialSummary, d as AppContextValue, r as AppPermissionsApi } from '../use-app-context-Byv_fam0.cjs';
|
|
3
|
+
export { b as AppBrandingDeclaration, z as Credential, P as PermissionCheckOptions, G as PermissionEntry } from '../use-app-context-Byv_fam0.cjs';
|
|
4
|
+
import { E as EnvironmentRef, B as TestConnectionResult } from '../pipeline-beeT8dje.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A platform Tool. Each installed app is upserted as a Tool keyed by its
|
|
@@ -18,8 +18,13 @@ interface Tool {
|
|
|
18
18
|
* when creating an inventory item, so call this once and pass the id as
|
|
19
19
|
* `toolId` to {@link addInventoryItem}. Returns null when no tool matches.
|
|
20
20
|
*
|
|
21
|
-
* GET /api/tools (
|
|
22
|
-
*
|
|
21
|
+
* GET /api/tools (paginated — `{ data, pagination }` — or a bare array; both are
|
|
22
|
+
* handled). We MUST filter server-side by name: the list is paginated (default 20,
|
|
23
|
+
* ordered by createdAt) so once a tenant has enough apps installed, a bare
|
|
24
|
+
* client-side find over just the first page silently misses a real tool whose row
|
|
25
|
+
* fell to a later page — surfacing a false "no <app> tool found, install the app
|
|
26
|
+
* first" error for an app that is in fact installed. `search` is a case-insensitive
|
|
27
|
+
* contains match, so we still exact-match `tool.name === name` on the result.
|
|
23
28
|
*/
|
|
24
29
|
declare function resolveTool(name: string): Promise<Tool | null>;
|
|
25
30
|
/** List the customer's inventory (deployment targets). GET /api/components */
|
|
@@ -71,6 +76,113 @@ declare function updateCredential(id: string, input: Partial<CredentialInput>):
|
|
|
71
76
|
/** Remove a credential. DELETE /api/credentials/:id. */
|
|
72
77
|
declare function removeCredential(id: string): Promise<void>;
|
|
73
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Test a Connection's endpoint + credential. The platform decrypts the secret,
|
|
81
|
+
* runs the owning app's connectivity-test handler in-process (the secret is
|
|
82
|
+
* never returned), and reports whether the endpoint + credentials actually work.
|
|
83
|
+
* A failed test resolves normally with `{ ok: false, message }` — it does not
|
|
84
|
+
* throw. Only transport/auth errors (401/404) surface as `{ ok: false }` too.
|
|
85
|
+
*
|
|
86
|
+
* @param appId the app that owns the connection, e.g. `splunk-cloud`
|
|
87
|
+
* @param credentialId the connection (credential) id
|
|
88
|
+
* @param opts.endpoint optional endpoint to test against instead of the one stored
|
|
89
|
+
* on the credential — e.g. an Access Server's reachable address
|
|
90
|
+
* (`<host>:<port>`). The host is scoped to the caller's tenant.
|
|
91
|
+
*/
|
|
92
|
+
declare function testConnection(appId: string, credentialId: string, opts?: {
|
|
93
|
+
endpoint?: string;
|
|
94
|
+
}): Promise<TestConnectionResult>;
|
|
95
|
+
/** Result of running an app operation. Mirrors the server operation handler's return. */
|
|
96
|
+
interface OperationResult {
|
|
97
|
+
ok: boolean;
|
|
98
|
+
message: string;
|
|
99
|
+
details?: string[];
|
|
100
|
+
/** Optional structured payload (e.g. an export download URL, a job id). */
|
|
101
|
+
data?: Record<string, unknown>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Run an app operation — a one-off action (restart, export, retry, …), NOT a
|
|
105
|
+
* configuration deploy. The platform decrypts the chosen connection's secret and
|
|
106
|
+
* runs the app's operation handler in-process (the secret is never returned). A
|
|
107
|
+
* failed operation resolves normally with `{ ok: false, message }` rather than
|
|
108
|
+
* throwing.
|
|
109
|
+
*
|
|
110
|
+
* @param appId the app that owns the operation, e.g. `splunk-cloud`
|
|
111
|
+
* @param operationId the operation id declared in the app manifest
|
|
112
|
+
* @param opts the connection (credentialId) to authenticate with, + params
|
|
113
|
+
*/
|
|
114
|
+
declare function runOperation(appId: string, operationId: string, opts?: {
|
|
115
|
+
credentialId?: string;
|
|
116
|
+
params?: Record<string, unknown>;
|
|
117
|
+
}): Promise<OperationResult>;
|
|
118
|
+
|
|
119
|
+
/** The client-safe onboarding descriptor an app advertises in /apps/enabled. */
|
|
120
|
+
interface AppOnboardingDescriptor {
|
|
121
|
+
provider: string;
|
|
122
|
+
label: string;
|
|
123
|
+
brokered?: boolean;
|
|
124
|
+
requiredSettings?: string[];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Fetch an app's connection-onboarding descriptor from GET /api/apps/enabled,
|
|
128
|
+
* so the Connections UI can offer an "Integrate via OAuth" option without the
|
|
129
|
+
* app page having to pass it in. Returns null when the app declares no
|
|
130
|
+
* onboarding (its connections stay credential-only).
|
|
131
|
+
*/
|
|
132
|
+
declare function fetchAppOnboarding(appId: string): Promise<AppOnboardingDescriptor | null>;
|
|
133
|
+
interface StartOnboardingInput {
|
|
134
|
+
/** The deployment scope (environment) the new connection belongs to. */
|
|
135
|
+
environmentId: string;
|
|
136
|
+
/** Display name for the new connection. */
|
|
137
|
+
connectionName: string;
|
|
138
|
+
/**
|
|
139
|
+
* Settings the admin supplies BEFORE consent (e.g. Sentinel's
|
|
140
|
+
* subscription_id / resource_group / workspace_name, or a cloud override).
|
|
141
|
+
*/
|
|
142
|
+
settings?: Record<string, unknown>;
|
|
143
|
+
}
|
|
144
|
+
interface StartOnboardingResult {
|
|
145
|
+
/** Open this in the browser — the provider's hosted consent page. */
|
|
146
|
+
authorizeUrl: string;
|
|
147
|
+
provider: string;
|
|
148
|
+
}
|
|
149
|
+
/** Begin onboarding a connection. POST /api/apps/:appId/connections/onboard/start. */
|
|
150
|
+
declare function startOnboarding(appId: string, input: StartOnboardingInput): Promise<StartOnboardingResult>;
|
|
151
|
+
interface OnboardingStatus {
|
|
152
|
+
provider: string;
|
|
153
|
+
brokered: boolean;
|
|
154
|
+
tenantId: string | null;
|
|
155
|
+
cloud: string;
|
|
156
|
+
manualSteps: Array<{
|
|
157
|
+
type: string;
|
|
158
|
+
title: string;
|
|
159
|
+
description: string;
|
|
160
|
+
deepLink?: string;
|
|
161
|
+
cliCommand?: string;
|
|
162
|
+
verifiable: boolean;
|
|
163
|
+
}>;
|
|
164
|
+
verify?: {
|
|
165
|
+
effective: boolean;
|
|
166
|
+
message: string;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Read onboarding status for a connection, optionally running the verify probe
|
|
171
|
+
* for pending manual steps (e.g. the Sentinel role assignment).
|
|
172
|
+
* GET /api/apps/:appId/connections/:credentialId/onboard/status.
|
|
173
|
+
*/
|
|
174
|
+
declare function getOnboardingStatus(appId: string, credentialId: string, opts?: {
|
|
175
|
+
verify?: boolean;
|
|
176
|
+
}): Promise<OnboardingStatus>;
|
|
177
|
+
/**
|
|
178
|
+
* Disconnect an onboarded connection (best-effort tenant-side teardown, then
|
|
179
|
+
* delete). POST /api/apps/:appId/connections/:credentialId/onboard/revoke.
|
|
180
|
+
*/
|
|
181
|
+
declare function revokeOnboarding(appId: string, credentialId: string): Promise<{
|
|
182
|
+
cleaned: boolean;
|
|
183
|
+
message: string;
|
|
184
|
+
}>;
|
|
185
|
+
|
|
74
186
|
/** Name of the global the platform installs before loading app bundles. */
|
|
75
187
|
declare const HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
76
188
|
/**
|
|
@@ -141,4 +253,4 @@ interface AppClientModule {
|
|
|
141
253
|
sidebarItems?: AppSidebarItem[];
|
|
142
254
|
}
|
|
143
255
|
|
|
144
|
-
export { type AppClientModule, AppPermissionsApi, type AppSidebarItem, ConnectivityProviderRef, CredentialInput, CredentialSummary, EnvironmentRef, HOST_RUNTIME_GLOBAL, InventoryItem, InventoryItemInput, type Tool, type VeltrixHostRuntime, addInventoryItem, authFetch, createCredential, getHostRuntime, listConnectivityProviders, listCredentials, listEnvironments, listInventory, removeCredential, removeInventoryItem, requireHostRuntime, resolveTool, updateCredential, updateInventoryItem };
|
|
256
|
+
export { type AppClientModule, type AppOnboardingDescriptor, AppPermissionsApi, type AppSidebarItem, ConnectivityProviderRef, CredentialInput, CredentialSummary, EnvironmentRef, HOST_RUNTIME_GLOBAL, InventoryItem, InventoryItemInput, type OnboardingStatus, type OperationResult, type StartOnboardingInput, type StartOnboardingResult, TestConnectionResult, type Tool, type VeltrixHostRuntime, addInventoryItem, authFetch, createCredential, fetchAppOnboarding, getHostRuntime, getOnboardingStatus, listConnectivityProviders, listCredentials, listEnvironments, listInventory, removeCredential, removeInventoryItem, requireHostRuntime, resolveTool, revokeOnboarding, runOperation, startOnboarding, testConnection, updateCredential, updateInventoryItem };
|