@octopusdeploy/mcp-server 1.0.1 → 2.1.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 +237 -46
- package/dist/helpers/activeToolsetConfig.d.ts +4 -0
- package/dist/helpers/activeToolsetConfig.d.ts.map +1 -0
- package/dist/helpers/activeToolsetConfig.js +18 -0
- package/dist/helpers/activeToolsetConfig.js.map +1 -0
- package/dist/helpers/errorHandling.d.ts +38 -0
- package/dist/helpers/errorHandling.d.ts.map +1 -0
- package/dist/helpers/errorHandling.js +75 -0
- package/dist/helpers/errorHandling.js.map +1 -0
- package/dist/helpers/getClientConfigurationFromEnvironment.d.ts +2 -0
- package/dist/helpers/getClientConfigurationFromEnvironment.d.ts.map +1 -1
- package/dist/helpers/getClientConfigurationFromEnvironment.js +21 -5
- package/dist/helpers/getClientConfigurationFromEnvironment.js.map +1 -1
- package/dist/helpers/grepLines.d.ts +38 -0
- package/dist/helpers/grepLines.d.ts.map +1 -0
- package/dist/helpers/grepLines.js +65 -0
- package/dist/helpers/grepLines.js.map +1 -0
- package/dist/helpers/methodTier.d.ts +15 -0
- package/dist/helpers/methodTier.d.ts.map +1 -0
- package/dist/helpers/methodTier.js +25 -0
- package/dist/helpers/methodTier.js.map +1 -0
- package/dist/helpers/pathAllowlist.d.ts +27 -0
- package/dist/helpers/pathAllowlist.d.ts.map +1 -0
- package/dist/helpers/pathAllowlist.js +177 -0
- package/dist/helpers/pathAllowlist.js.map +1 -0
- package/dist/helpers/pathGlob.d.ts +15 -0
- package/dist/helpers/pathGlob.d.ts.map +1 -0
- package/dist/helpers/pathGlob.js +47 -0
- package/dist/helpers/pathGlob.js.map +1 -0
- package/dist/helpers/requireConfirmation.d.ts +119 -0
- package/dist/helpers/requireConfirmation.d.ts.map +1 -0
- package/dist/helpers/requireConfirmation.js +148 -0
- package/dist/helpers/requireConfirmation.js.map +1 -0
- package/dist/helpers/sensitivePathDenylist.d.ts +32 -0
- package/dist/helpers/sensitivePathDenylist.d.ts.map +1 -0
- package/dist/helpers/sensitivePathDenylist.js +49 -0
- package/dist/helpers/sensitivePathDenylist.js.map +1 -0
- package/dist/helpers/spaceResolver.d.ts +4 -0
- package/dist/helpers/spaceResolver.d.ts.map +1 -0
- package/dist/helpers/spaceResolver.js +18 -0
- package/dist/helpers/spaceResolver.js.map +1 -0
- package/dist/helpers/stripLinks.d.ts +15 -0
- package/dist/helpers/stripLinks.d.ts.map +1 -0
- package/dist/helpers/stripLinks.js +19 -0
- package/dist/helpers/stripLinks.js.map +1 -0
- package/dist/helpers/urlParser.d.ts +16 -0
- package/dist/helpers/urlParser.d.ts.map +1 -0
- package/dist/helpers/urlParser.js +83 -0
- package/dist/helpers/urlParser.js.map +1 -0
- package/dist/helpers/userCache.d.ts +14 -0
- package/dist/helpers/userCache.d.ts.map +1 -0
- package/dist/helpers/userCache.js +16 -0
- package/dist/helpers/userCache.js.map +1 -0
- package/dist/helpers/validateExecutePath.d.ts +29 -0
- package/dist/helpers/validateExecutePath.d.ts.map +1 -0
- package/dist/helpers/validateExecutePath.js +62 -0
- package/dist/helpers/validateExecutePath.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +64 -15
- package/dist/index.js.map +1 -1
- package/dist/resources/catalog/capabilities.d.ts +36 -0
- package/dist/resources/catalog/capabilities.d.ts.map +1 -0
- package/dist/resources/catalog/capabilities.js +90 -0
- package/dist/resources/catalog/capabilities.js.map +1 -0
- package/dist/resources/catalog/llmsTxt.d.ts +8 -0
- package/dist/resources/catalog/llmsTxt.d.ts.map +1 -0
- package/dist/resources/catalog/llmsTxt.js +44 -0
- package/dist/resources/catalog/llmsTxt.js.map +1 -0
- package/dist/resources/dispatch.d.ts +30 -0
- package/dist/resources/dispatch.d.ts.map +1 -0
- package/dist/resources/dispatch.js +81 -0
- package/dist/resources/dispatch.js.map +1 -0
- package/dist/resources/featureToggle.d.ts +2 -0
- package/dist/resources/featureToggle.d.ts.map +1 -0
- package/dist/resources/featureToggle.js +34 -0
- package/dist/resources/featureToggle.js.map +1 -0
- package/dist/resources/index.d.ts +12 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +44 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/interruption.d.ts +2 -0
- package/dist/resources/interruption.d.ts.map +1 -0
- package/dist/resources/interruption.js +34 -0
- package/dist/resources/interruption.js.map +1 -0
- package/dist/resources/release.d.ts +2 -0
- package/dist/resources/release.d.ts.map +1 -0
- package/dist/resources/release.js +33 -0
- package/dist/resources/release.js.map +1 -0
- package/dist/resources/rolloutGroup.d.ts +2 -0
- package/dist/resources/rolloutGroup.d.ts.map +1 -0
- package/dist/resources/rolloutGroup.js +35 -0
- package/dist/resources/rolloutGroup.js.map +1 -0
- package/dist/resources/runbook.d.ts +2 -0
- package/dist/resources/runbook.d.ts.map +1 -0
- package/dist/resources/runbook.js +34 -0
- package/dist/resources/runbook.js.map +1 -0
- package/dist/resources/task.d.ts +2 -0
- package/dist/resources/task.d.ts.map +1 -0
- package/dist/resources/task.js +68 -0
- package/dist/resources/task.js.map +1 -0
- package/dist/tools/createRelease.d.ts +3 -0
- package/dist/tools/createRelease.d.ts.map +1 -0
- package/dist/tools/createRelease.js +151 -0
- package/dist/tools/createRelease.js.map +1 -0
- package/dist/tools/deployRelease.d.ts +3 -0
- package/dist/tools/deployRelease.d.ts.map +1 -0
- package/dist/tools/deployRelease.js +227 -0
- package/dist/tools/deployRelease.js.map +1 -0
- package/dist/tools/execute.d.ts +3 -0
- package/dist/tools/execute.d.ts.map +1 -0
- package/dist/tools/execute.js +261 -0
- package/dist/tools/execute.js.map +1 -0
- package/dist/tools/findAccounts.d.ts +3 -0
- package/dist/tools/findAccounts.d.ts.map +1 -0
- package/dist/tools/findAccounts.js +90 -0
- package/dist/tools/findAccounts.js.map +1 -0
- package/dist/tools/findCertificates.d.ts +3 -0
- package/dist/tools/findCertificates.d.ts.map +1 -0
- package/dist/tools/findCertificates.js +98 -0
- package/dist/tools/findCertificates.js.map +1 -0
- package/dist/tools/findDeploymentTargets.d.ts +3 -0
- package/dist/tools/findDeploymentTargets.d.ts.map +1 -0
- package/dist/tools/findDeploymentTargets.js +161 -0
- package/dist/tools/findDeploymentTargets.js.map +1 -0
- package/dist/tools/findFeatureToggles.d.ts +37 -0
- package/dist/tools/findFeatureToggles.d.ts.map +1 -0
- package/dist/tools/findFeatureToggles.js +139 -0
- package/dist/tools/findFeatureToggles.js.map +1 -0
- package/dist/tools/findInterruptions.d.ts +79 -0
- package/dist/tools/findInterruptions.d.ts.map +1 -0
- package/dist/tools/findInterruptions.js +273 -0
- package/dist/tools/findInterruptions.js.map +1 -0
- package/dist/tools/findReleases.d.ts +3 -0
- package/dist/tools/findReleases.d.ts.map +1 -0
- package/dist/tools/findReleases.js +138 -0
- package/dist/tools/findReleases.js.map +1 -0
- package/dist/tools/findRunbooks.d.ts +3 -0
- package/dist/tools/findRunbooks.d.ts.map +1 -0
- package/dist/tools/findRunbooks.js +139 -0
- package/dist/tools/findRunbooks.js.map +1 -0
- package/dist/tools/findTenants.d.ts +19 -0
- package/dist/tools/findTenants.d.ts.map +1 -0
- package/dist/tools/findTenants.js +133 -0
- package/dist/tools/findTenants.js.map +1 -0
- package/dist/tools/getBranches.d.ts.map +1 -1
- package/dist/tools/getBranches.js +51 -33
- package/dist/tools/getBranches.js.map +1 -1
- package/dist/tools/getCurrentUser.d.ts.map +1 -1
- package/dist/tools/getCurrentUser.js +34 -25
- package/dist/tools/getCurrentUser.js.map +1 -1
- package/dist/tools/getDeploymentFromUrl.d.ts +57 -0
- package/dist/tools/getDeploymentFromUrl.d.ts.map +1 -0
- package/dist/tools/getDeploymentFromUrl.js +162 -0
- package/dist/tools/getDeploymentFromUrl.js.map +1 -0
- package/dist/tools/getDeploymentProcess.d.ts.map +1 -1
- package/dist/tools/getDeploymentProcess.js +57 -19
- package/dist/tools/getDeploymentProcess.js.map +1 -1
- package/dist/tools/getKubernetesLiveStatus.d.ts.map +1 -1
- package/dist/tools/getKubernetesLiveStatus.js +70 -48
- package/dist/tools/getKubernetesLiveStatus.js.map +1 -1
- package/dist/tools/getMissingTenantVariables.d.ts.map +1 -1
- package/dist/tools/getMissingTenantVariables.js +58 -26
- package/dist/tools/getMissingTenantVariables.js.map +1 -1
- package/dist/tools/getTaskFromUrl.d.ts +18 -0
- package/dist/tools/getTaskFromUrl.d.ts.map +1 -0
- package/dist/tools/getTaskFromUrl.js +90 -0
- package/dist/tools/getTaskFromUrl.js.map +1 -0
- package/dist/tools/getTenantVariables.d.ts.map +1 -1
- package/dist/tools/getTenantVariables.js +58 -38
- package/dist/tools/getTenantVariables.js.map +1 -1
- package/dist/tools/getVariables.d.ts.map +1 -1
- package/dist/tools/getVariables.js +12 -9
- package/dist/tools/getVariables.js.map +1 -1
- package/dist/tools/grepLlmsTxt.d.ts +13 -0
- package/dist/tools/grepLlmsTxt.d.ts.map +1 -0
- package/dist/tools/grepLlmsTxt.js +105 -0
- package/dist/tools/grepLlmsTxt.js.map +1 -0
- package/dist/tools/grepTaskLog.d.ts +30 -0
- package/dist/tools/grepTaskLog.d.ts.map +1 -0
- package/dist/tools/grepTaskLog.js +116 -0
- package/dist/tools/grepTaskLog.js.map +1 -0
- package/dist/tools/index.d.ts +22 -18
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +33 -21
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/listDeployments.d.ts.map +1 -1
- package/dist/tools/listDeployments.js +77 -37
- package/dist/tools/listDeployments.js.map +1 -1
- package/dist/tools/listEnvironments.d.ts.map +1 -1
- package/dist/tools/listEnvironments.js +63 -37
- package/dist/tools/listEnvironments.js.map +1 -1
- package/dist/tools/listProjects.d.ts.map +1 -1
- package/dist/tools/listProjects.js +64 -38
- package/dist/tools/listProjects.js.map +1 -1
- package/dist/tools/listSpaces.d.ts.map +1 -1
- package/dist/tools/listSpaces.js +56 -30
- package/dist/tools/listSpaces.js.map +1 -1
- package/dist/tools/readResource.d.ts +3 -0
- package/dist/tools/readResource.d.ts.map +1 -0
- package/dist/tools/readResource.js +50 -0
- package/dist/tools/readResource.js.map +1 -0
- package/dist/tools/runRunbook.d.ts +3 -0
- package/dist/tools/runRunbook.d.ts.map +1 -0
- package/dist/tools/runRunbook.js +174 -0
- package/dist/tools/runRunbook.js.map +1 -0
- package/dist/tools/updateFeatureToggle.d.ts +94 -0
- package/dist/tools/updateFeatureToggle.d.ts.map +1 -0
- package/dist/tools/updateFeatureToggle.js +308 -0
- package/dist/tools/updateFeatureToggle.js.map +1 -0
- package/dist/types/featureToggleTypes.d.ts +47 -0
- package/dist/types/featureToggleTypes.d.ts.map +1 -0
- package/dist/types/featureToggleTypes.js +10 -0
- package/dist/types/featureToggleTypes.js.map +1 -0
- package/dist/types/resourceConfig.d.ts +17 -0
- package/dist/types/resourceConfig.d.ts.map +1 -0
- package/dist/types/resourceConfig.js +6 -0
- package/dist/types/resourceConfig.js.map +1 -0
- package/dist/types/toolAnnotations.d.ts +8 -0
- package/dist/types/toolAnnotations.d.ts.map +1 -0
- package/dist/types/toolAnnotations.js +28 -0
- package/dist/types/toolAnnotations.js.map +1 -0
- package/dist/types/toolConfig.d.ts +7 -1
- package/dist/types/toolConfig.d.ts.map +1 -1
- package/dist/types/toolConfig.js +4 -1
- package/dist/types/toolConfig.js.map +1 -1
- package/dist/utils/parseConfig.d.ts +1 -1
- package/dist/utils/parseConfig.d.ts.map +1 -1
- package/dist/utils/parseConfig.js +3 -2
- package/dist/utils/parseConfig.js.map +1 -1
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +3 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +9 -3
- package/dist/tools/getAccount.d.ts +0 -3
- package/dist/tools/getAccount.d.ts.map +0 -1
- package/dist/tools/getAccount.js +0 -40
- package/dist/tools/getAccount.js.map +0 -1
- package/dist/tools/getCertificate.d.ts +0 -3
- package/dist/tools/getCertificate.d.ts.map +0 -1
- package/dist/tools/getCertificate.js +0 -40
- package/dist/tools/getCertificate.js.map +0 -1
- package/dist/tools/getDeploymentTarget.d.ts +0 -3
- package/dist/tools/getDeploymentTarget.d.ts.map +0 -1
- package/dist/tools/getDeploymentTarget.js +0 -66
- package/dist/tools/getDeploymentTarget.js.map +0 -1
- package/dist/tools/getReleaseById.d.ts +0 -3
- package/dist/tools/getReleaseById.d.ts.map +0 -1
- package/dist/tools/getReleaseById.js +0 -45
- package/dist/tools/getReleaseById.js.map +0 -1
- package/dist/tools/getTaskById.d.ts +0 -9
- package/dist/tools/getTaskById.d.ts.map +0 -1
- package/dist/tools/getTaskById.js +0 -44
- package/dist/tools/getTaskById.js.map +0 -1
- package/dist/tools/getTaskDetails.d.ts +0 -9
- package/dist/tools/getTaskDetails.d.ts.map +0 -1
- package/dist/tools/getTaskDetails.js +0 -44
- package/dist/tools/getTaskDetails.js.map +0 -1
- package/dist/tools/getTaskRaw.d.ts +0 -9
- package/dist/tools/getTaskRaw.d.ts.map +0 -1
- package/dist/tools/getTaskRaw.js +0 -43
- package/dist/tools/getTaskRaw.js.map +0 -1
- package/dist/tools/getTenantById.d.ts +0 -3
- package/dist/tools/getTenantById.d.ts.map +0 -1
- package/dist/tools/getTenantById.js +0 -50
- package/dist/tools/getTenantById.js.map +0 -1
- package/dist/tools/listAccounts.d.ts +0 -3
- package/dist/tools/listAccounts.d.ts.map +0 -1
- package/dist/tools/listAccounts.js +0 -54
- package/dist/tools/listAccounts.js.map +0 -1
- package/dist/tools/listCertificates.d.ts +0 -3
- package/dist/tools/listCertificates.d.ts.map +0 -1
- package/dist/tools/listCertificates.js +0 -62
- package/dist/tools/listCertificates.js.map +0 -1
- package/dist/tools/listDeploymentTargets.d.ts +0 -3
- package/dist/tools/listDeploymentTargets.d.ts.map +0 -1
- package/dist/tools/listDeploymentTargets.js +0 -98
- package/dist/tools/listDeploymentTargets.js.map +0 -1
- package/dist/tools/listReleases.d.ts +0 -3
- package/dist/tools/listReleases.d.ts.map +0 -1
- package/dist/tools/listReleases.js +0 -54
- package/dist/tools/listReleases.js.map +0 -1
- package/dist/tools/listReleasesForProject.d.ts +0 -3
- package/dist/tools/listReleasesForProject.d.ts.map +0 -1
- package/dist/tools/listReleasesForProject.js +0 -60
- package/dist/tools/listReleasesForProject.js.map +0 -1
- package/dist/tools/listTenants.d.ts +0 -3
- package/dist/tools/listTenants.d.ts.map +0 -1
- package/dist/tools/listTenants.js +0 -69
- package/dist/tools/listTenants.js.map +0 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {} from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { env } from "process";
|
|
3
|
+
function renderChange(change) {
|
|
4
|
+
// Preserve source order first, then append target-only keys, so related
|
|
5
|
+
// fields stay together rather than getting alphabetised apart.
|
|
6
|
+
const seen = new Set();
|
|
7
|
+
const keys = [];
|
|
8
|
+
for (const k of Object.keys(change.source)) {
|
|
9
|
+
keys.push(k);
|
|
10
|
+
seen.add(k);
|
|
11
|
+
}
|
|
12
|
+
for (const k of Object.keys(change.target)) {
|
|
13
|
+
if (!seen.has(k))
|
|
14
|
+
keys.push(k);
|
|
15
|
+
}
|
|
16
|
+
const entries = [];
|
|
17
|
+
const buildEntry = (marker, key, value) => {
|
|
18
|
+
const valueLines = JSON.stringify(value, null, 2).split("\n");
|
|
19
|
+
const lines = [` ${JSON.stringify(key)}: ${valueLines[0]}`];
|
|
20
|
+
for (let i = 1; i < valueLines.length; i++) {
|
|
21
|
+
lines.push(` ${valueLines[i]}`);
|
|
22
|
+
}
|
|
23
|
+
return { marker, lines };
|
|
24
|
+
};
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
const inSource = key in change.source;
|
|
27
|
+
const inTarget = key in change.target;
|
|
28
|
+
const sourceJson = inSource
|
|
29
|
+
? JSON.stringify(change.source[key], null, 2)
|
|
30
|
+
: undefined;
|
|
31
|
+
const targetJson = inTarget
|
|
32
|
+
? JSON.stringify(change.target[key], null, 2)
|
|
33
|
+
: undefined;
|
|
34
|
+
if (sourceJson === targetJson)
|
|
35
|
+
continue;
|
|
36
|
+
if (inSource)
|
|
37
|
+
entries.push(buildEntry("-", key, change.source[key]));
|
|
38
|
+
if (inTarget)
|
|
39
|
+
entries.push(buildEntry("+", key, change.target[key]));
|
|
40
|
+
}
|
|
41
|
+
if (entries.length === 0)
|
|
42
|
+
return "{}";
|
|
43
|
+
const out = ["{"];
|
|
44
|
+
entries.forEach((entry, idx) => {
|
|
45
|
+
const isLastEntry = idx === entries.length - 1;
|
|
46
|
+
entry.lines.forEach((line, lineIdx) => {
|
|
47
|
+
const isLastLineOfEntry = lineIdx === entry.lines.length - 1;
|
|
48
|
+
const suffix = isLastLineOfEntry && !isLastEntry ? "," : "";
|
|
49
|
+
out.push(`${entry.marker}${line}${suffix}`);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
out.push("}");
|
|
53
|
+
return out.join("\n");
|
|
54
|
+
}
|
|
55
|
+
function buildConfirmationMessage(message, change) {
|
|
56
|
+
return change ? `${message}\n\n${renderChange(change)}` : message;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Gate a write/destructive tool call on explicit user confirmation.
|
|
60
|
+
*
|
|
61
|
+
* Resolution order:
|
|
62
|
+
* 1. `OCTOPUS_SKIP_ELICITATION=true` env var → bypass (automation/CI).
|
|
63
|
+
* 2. Client advertises elicitation capability → SDK emits `elicitation/create`
|
|
64
|
+
* and we map `result.action` to accepted/declined/cancelled.
|
|
65
|
+
* 3. Client does not advertise elicitation → fall back to the `confirm` arg
|
|
66
|
+
* the tool surfaced in its own input schema. Distinguishes between
|
|
67
|
+
* explicit `false` (declined) and missing (confirmationRequired) so the
|
|
68
|
+
* caller can surface the latter as a hard error.
|
|
69
|
+
*/
|
|
70
|
+
export async function requireConfirmation(server, opts) {
|
|
71
|
+
if (env["OCTOPUS_SKIP_ELICITATION"] === "true") {
|
|
72
|
+
return { confirmed: true, reason: "envSkip" };
|
|
73
|
+
}
|
|
74
|
+
const capabilities = server.server.getClientCapabilities();
|
|
75
|
+
if (capabilities?.elicitation) {
|
|
76
|
+
const result = await server.server.elicitInput({
|
|
77
|
+
mode: "form",
|
|
78
|
+
message: buildConfirmationMessage(opts.message, opts.change),
|
|
79
|
+
// Empty properties → most clients render as a plain Accept/Decline prompt.
|
|
80
|
+
requestedSchema: { type: "object", properties: {} },
|
|
81
|
+
});
|
|
82
|
+
switch (result.action) {
|
|
83
|
+
case "accept":
|
|
84
|
+
return { confirmed: true, reason: "accepted" };
|
|
85
|
+
case "decline":
|
|
86
|
+
return { confirmed: false, reason: "declined" };
|
|
87
|
+
case "cancel":
|
|
88
|
+
default:
|
|
89
|
+
return { confirmed: false, reason: "cancelled" };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (opts.fallbackConfirm === true) {
|
|
93
|
+
return { confirmed: true, reason: "fallbackConfirm" };
|
|
94
|
+
}
|
|
95
|
+
if (opts.fallbackConfirm === false) {
|
|
96
|
+
return { confirmed: false, reason: "declined" };
|
|
97
|
+
}
|
|
98
|
+
return { confirmed: false, reason: "confirmationRequired" };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Build the standard tool response for a non-confirmed gate result.
|
|
102
|
+
*
|
|
103
|
+
* - `confirmationRequired` → `isError: true` with directive prose telling the
|
|
104
|
+
* LLM to ask the user before retrying with `confirm: true`. This is the
|
|
105
|
+
* "user was never asked" branch — distinct from a real cancellation, and
|
|
106
|
+
* marked as an error so the LLM doesn't paper over it.
|
|
107
|
+
* - `declined` / `cancelled` → soft cancellation shape with the original
|
|
108
|
+
* reason preserved for telemetry.
|
|
109
|
+
*
|
|
110
|
+
* Centralized here so every gated tool produces identical responses; the only
|
|
111
|
+
* thing a caller varies is the `action` noun. Return type is inferred so it
|
|
112
|
+
* stays compatible with the SDK's tool-handler return shape (which carries an
|
|
113
|
+
* `[key: string]: unknown` index signature we don't want to redeclare).
|
|
114
|
+
*/
|
|
115
|
+
export function unconfirmedResponse(result, opts) {
|
|
116
|
+
if (result.reason === "confirmationRequired") {
|
|
117
|
+
return {
|
|
118
|
+
content: [
|
|
119
|
+
{
|
|
120
|
+
type: "text",
|
|
121
|
+
text: JSON.stringify({
|
|
122
|
+
success: false,
|
|
123
|
+
confirmationRequired: true,
|
|
124
|
+
message: `This MCP client does not support elicitation, so the server cannot prompt the user to confirm this ${opts.action} directly. ` +
|
|
125
|
+
`The user has NOT been asked. Stop and ask the user explicitly whether to proceed; if they approve, retry the call with confirm: true. ` +
|
|
126
|
+
`Do not pass confirm: true without their explicit approval.`,
|
|
127
|
+
}, null, 2),
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
isError: true,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const capitalized = opts.action.charAt(0).toUpperCase() + opts.action.slice(1);
|
|
134
|
+
return {
|
|
135
|
+
content: [
|
|
136
|
+
{
|
|
137
|
+
type: "text",
|
|
138
|
+
text: JSON.stringify({
|
|
139
|
+
success: false,
|
|
140
|
+
cancelled: true,
|
|
141
|
+
reason: result.reason,
|
|
142
|
+
message: `${capitalized} cancelled by user.`,
|
|
143
|
+
}, null, 2),
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=requireConfirmation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requireConfirmation.js","sourceRoot":"","sources":["../../src/helpers/requireConfirmation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hard denylist for `execute` paths that must never be reachable, regardless
|
|
3
|
+
* of toolsets or mode flags. These are entries the user cannot opt into via
|
|
4
|
+
* --allow-deletes, and remain blocked even when --read-only is not set.
|
|
5
|
+
*
|
|
6
|
+
* Two categories live here:
|
|
7
|
+
* 1. API-key management — creating or modifying API keys via the MCP server
|
|
8
|
+
* is structurally out of scope (the agent uses a key already; minting more
|
|
9
|
+
* from inside the agent is a privilege escalation pattern).
|
|
10
|
+
* 2. Catastrophic deletes — paths whose DELETE removes a top-level container
|
|
11
|
+
* (a Space, a User) and is operationally irreversible. Allowing the agent
|
|
12
|
+
* to issue these from a single tool call is too sharp an edge.
|
|
13
|
+
*
|
|
14
|
+
* Pattern syntax is the shared `compilePathGlob` engine: `*` matches one
|
|
15
|
+
* segment, `**` matches across segments, and every other character is literal.
|
|
16
|
+
*/
|
|
17
|
+
import { type HttpMethod } from "./methodTier.js";
|
|
18
|
+
export interface DenyEntry {
|
|
19
|
+
/** Optional method filter; absent = all methods. */
|
|
20
|
+
method?: HttpMethod;
|
|
21
|
+
/** Glob pattern. `*` = one segment, `**` = any number of segments. */
|
|
22
|
+
pattern: string;
|
|
23
|
+
/** Short reason surfaced in the error response. */
|
|
24
|
+
reason: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const SENSITIVE_PATHS: readonly DenyEntry[];
|
|
27
|
+
export interface SensitiveCheckResult {
|
|
28
|
+
blocked: boolean;
|
|
29
|
+
reason?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function isSensitive(method: HttpMethod, path: string, entries?: readonly DenyEntry[]): SensitiveCheckResult;
|
|
32
|
+
//# sourceMappingURL=sensitivePathDenylist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitivePathDenylist.d.ts","sourceRoot":"","sources":["../../src/helpers/sensitivePathDenylist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,MAAM,WAAW,SAAS;IACxB,oDAAoD;IACpD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,eAAe,EAAE,SAAS,SAAS,EAuB/C,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,SAAS,SAAS,EAAoB,GAC9C,oBAAoB,CAQtB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hard denylist for `execute` paths that must never be reachable, regardless
|
|
3
|
+
* of toolsets or mode flags. These are entries the user cannot opt into via
|
|
4
|
+
* --allow-deletes, and remain blocked even when --read-only is not set.
|
|
5
|
+
*
|
|
6
|
+
* Two categories live here:
|
|
7
|
+
* 1. API-key management — creating or modifying API keys via the MCP server
|
|
8
|
+
* is structurally out of scope (the agent uses a key already; minting more
|
|
9
|
+
* from inside the agent is a privilege escalation pattern).
|
|
10
|
+
* 2. Catastrophic deletes — paths whose DELETE removes a top-level container
|
|
11
|
+
* (a Space, a User) and is operationally irreversible. Allowing the agent
|
|
12
|
+
* to issue these from a single tool call is too sharp an edge.
|
|
13
|
+
*
|
|
14
|
+
* Pattern syntax is the shared `compilePathGlob` engine: `*` matches one
|
|
15
|
+
* segment, `**` matches across segments, and every other character is literal.
|
|
16
|
+
*/
|
|
17
|
+
import {} from "./methodTier.js";
|
|
18
|
+
import { pathMatchesGlob } from "./pathGlob.js";
|
|
19
|
+
export const SENSITIVE_PATHS = [
|
|
20
|
+
{
|
|
21
|
+
pattern: "/api/users/*/apikeys",
|
|
22
|
+
reason: "API key management is out of scope for the MCP server. Use the Octopus web portal.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
pattern: "/api/users/*/apikeys/**",
|
|
26
|
+
reason: "API key management is out of scope for the MCP server. Use the Octopus web portal.",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
method: "DELETE",
|
|
30
|
+
pattern: "/api/users/*",
|
|
31
|
+
reason: "Deleting a user is irreversible and out of scope for the MCP server.",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
method: "DELETE",
|
|
35
|
+
pattern: "/api/spaces/*",
|
|
36
|
+
reason: "Deleting a Space is catastrophic and out of scope for the MCP server.",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
export function isSensitive(method, path, entries = SENSITIVE_PATHS) {
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
if (entry.method && entry.method !== method)
|
|
42
|
+
continue;
|
|
43
|
+
if (pathMatchesGlob(path, entry.pattern)) {
|
|
44
|
+
return { blocked: true, reason: entry.reason };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { blocked: false };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=sensitivePathDenylist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitivePathDenylist.js","sourceRoot":"","sources":["../../src/helpers/sensitivePathDenylist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAmB,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAWhD,MAAM,CAAC,MAAM,eAAe,GAAyB;IACnD;QACE,OAAO,EAAE,sBAAsB;QAC/B,MAAM,EACJ,oFAAoF;KACvF;IACD;QACE,OAAO,EAAE,yBAAyB;QAClC,MAAM,EACJ,oFAAoF;KACvF;IACD;QACE,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,cAAc;QACvB,MAAM,EACJ,sEAAsE;KACzE;IACD;QACE,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,eAAe;QACxB,MAAM,EACJ,uEAAuE;KAC1E;CACF,CAAC;AAOF,MAAM,UAAU,WAAW,CACzB,MAAkB,EAClB,IAAY,EACZ,UAAgC,eAAe;IAE/C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;YAAE,SAAS;QACtD,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spaceResolver.d.ts","sourceRoot":"","sources":["../../src/helpers/spaceResolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAmB,MAAM,2BAA2B,CAAC;AAKpE,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Client, SpaceRepository } from "@octopusdeploy/api-client";
|
|
2
|
+
const spaceCache = new Map();
|
|
3
|
+
const CACHE_TTL = 3600000; // 1 hour in milliseconds
|
|
4
|
+
export async function resolveSpaceNameFromId(client, spaceId) {
|
|
5
|
+
const cached = spaceCache.get(spaceId);
|
|
6
|
+
const now = Date.now();
|
|
7
|
+
if (cached && now - cached.timestamp < CACHE_TTL) {
|
|
8
|
+
return cached.name;
|
|
9
|
+
}
|
|
10
|
+
const spaceRepository = new SpaceRepository(client);
|
|
11
|
+
const space = await spaceRepository.get(spaceId);
|
|
12
|
+
spaceCache.set(spaceId, { name: space.Name, timestamp: now });
|
|
13
|
+
return space.Name;
|
|
14
|
+
}
|
|
15
|
+
export function clearSpaceCache() {
|
|
16
|
+
spaceCache.clear();
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=spaceResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spaceResolver.js","sourceRoot":"","sources":["../../src/helpers/spaceResolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEpE,MAAM,UAAU,GAAG,IAAI,GAAG,EAA+C,CAAC;AAC1E,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,yBAAyB;AAEpD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,OAAe;IAEf,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,IAAI,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC;QACjD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAEjD,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAE9D,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,UAAU,CAAC,KAAK,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip the HATEOAS `Links` bag from an Octopus API response object.
|
|
3
|
+
*
|
|
4
|
+
* Octopus REST responses include a `Links` map of hypermedia URLs (self, related
|
|
5
|
+
* collections, etc.) that an LLM consumer doesn't need and that bloats the payload.
|
|
6
|
+
* Every resource handler that returns an api-client object as JSON should pass it
|
|
7
|
+
* through this helper first.
|
|
8
|
+
*
|
|
9
|
+
* The api-client typings often don't include `Links` on their response interfaces
|
|
10
|
+
* (the runtime payload has it; the typings don't always declare it), which is why
|
|
11
|
+
* the input is widened to `Record<string, unknown>` rather than typed against a
|
|
12
|
+
* specific resource interface.
|
|
13
|
+
*/
|
|
14
|
+
export declare function stripLinks(resource: object): Record<string, unknown>;
|
|
15
|
+
//# sourceMappingURL=stripLinks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripLinks.d.ts","sourceRoot":"","sources":["../../src/helpers/stripLinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIpE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip the HATEOAS `Links` bag from an Octopus API response object.
|
|
3
|
+
*
|
|
4
|
+
* Octopus REST responses include a `Links` map of hypermedia URLs (self, related
|
|
5
|
+
* collections, etc.) that an LLM consumer doesn't need and that bloats the payload.
|
|
6
|
+
* Every resource handler that returns an api-client object as JSON should pass it
|
|
7
|
+
* through this helper first.
|
|
8
|
+
*
|
|
9
|
+
* The api-client typings often don't include `Links` on their response interfaces
|
|
10
|
+
* (the runtime payload has it; the typings don't always declare it), which is why
|
|
11
|
+
* the input is widened to `Record<string, unknown>` rather than typed against a
|
|
12
|
+
* specific resource interface.
|
|
13
|
+
*/
|
|
14
|
+
export function stripLinks(resource) {
|
|
15
|
+
const copy = { ...resource };
|
|
16
|
+
delete copy.Links;
|
|
17
|
+
return copy;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=stripLinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripLinks.js","sourceRoot":"","sources":["../../src/helpers/stripLinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,MAAM,IAAI,GAA4B,EAAE,GAAG,QAAQ,EAA6B,CAAC;IACjF,OAAO,IAAI,CAAC,KAAK,CAAC;IAClB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface OctopusUrlParts {
|
|
2
|
+
serverUrl: string;
|
|
3
|
+
spaceId?: string;
|
|
4
|
+
resourceType?: "deployment" | "release" | "project" | "tenant" | "task" | "unknown";
|
|
5
|
+
resourceId?: string;
|
|
6
|
+
projectSlug?: string;
|
|
7
|
+
releaseVersion?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseOctopusUrl(url: string): OctopusUrlParts;
|
|
10
|
+
export declare function extractDeploymentId(url: string): string | null;
|
|
11
|
+
export declare function extractTaskId(url: string): string | null;
|
|
12
|
+
export declare function extractSpaceId(url: string): string | null;
|
|
13
|
+
export declare function extractProjectSlug(url: string): string | null;
|
|
14
|
+
export declare function extractReleaseVersion(url: string): string | null;
|
|
15
|
+
export declare function extractTenantId(url: string): string | null;
|
|
16
|
+
//# sourceMappingURL=urlParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urlParser.d.ts","sourceRoot":"","sources":["../../src/helpers/urlParser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAwD5D;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGxD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG7D;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG1D"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export function parseOctopusUrl(url) {
|
|
2
|
+
try {
|
|
3
|
+
const urlObj = new URL(url);
|
|
4
|
+
const serverUrl = `${urlObj.protocol}//${urlObj.host}`;
|
|
5
|
+
const hashPath = urlObj.hash.startsWith("#/") ? urlObj.hash.substring(2) : urlObj.hash.substring(1);
|
|
6
|
+
const pathParts = hashPath.split("/").filter(Boolean);
|
|
7
|
+
const result = {
|
|
8
|
+
serverUrl,
|
|
9
|
+
};
|
|
10
|
+
const spaceId = extractSpaceId(url);
|
|
11
|
+
if (spaceId) {
|
|
12
|
+
result.spaceId = spaceId;
|
|
13
|
+
}
|
|
14
|
+
if (pathParts.includes("deployments")) {
|
|
15
|
+
const deploymentId = extractDeploymentId(url);
|
|
16
|
+
if (deploymentId) {
|
|
17
|
+
result.resourceType = "deployment";
|
|
18
|
+
result.resourceId = deploymentId;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const releaseVersion = extractReleaseVersion(url);
|
|
22
|
+
if (releaseVersion) {
|
|
23
|
+
result.resourceType = "release";
|
|
24
|
+
result.resourceId = releaseVersion;
|
|
25
|
+
result.releaseVersion = releaseVersion;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (pathParts.includes("tasks")) {
|
|
30
|
+
const taskId = extractTaskId(url);
|
|
31
|
+
if (taskId) {
|
|
32
|
+
result.resourceType = "task";
|
|
33
|
+
result.resourceId = taskId;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else if (pathParts.includes("projects")) {
|
|
37
|
+
result.resourceType = "project";
|
|
38
|
+
const projectSlug = extractProjectSlug(url);
|
|
39
|
+
if (projectSlug) {
|
|
40
|
+
result.projectSlug = projectSlug;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (pathParts.includes("tenants")) {
|
|
44
|
+
const tenantId = extractTenantId(url);
|
|
45
|
+
if (tenantId) {
|
|
46
|
+
result.resourceType = "tenant";
|
|
47
|
+
result.resourceId = tenantId;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
result.resourceType = "unknown";
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new Error(`Failed to parse Octopus URL: ${error instanceof Error ? error.message : String(error)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function extractDeploymentId(url) {
|
|
60
|
+
const match = url.match(/\/deployments\/(Deployments-\d+)/);
|
|
61
|
+
return match ? match[1] : null;
|
|
62
|
+
}
|
|
63
|
+
export function extractTaskId(url) {
|
|
64
|
+
const match = url.match(/\/tasks\/(ServerTasks-\d+)/);
|
|
65
|
+
return match ? match[1] : null;
|
|
66
|
+
}
|
|
67
|
+
export function extractSpaceId(url) {
|
|
68
|
+
const match = url.match(/\/(Spaces-\d+)/);
|
|
69
|
+
return match ? match[1] : null;
|
|
70
|
+
}
|
|
71
|
+
export function extractProjectSlug(url) {
|
|
72
|
+
const match = url.match(/\/projects\/([^/]+)/);
|
|
73
|
+
return match ? match[1] : null;
|
|
74
|
+
}
|
|
75
|
+
export function extractReleaseVersion(url) {
|
|
76
|
+
const match = url.match(/\/releases\/([^/]+)/);
|
|
77
|
+
return match ? match[1] : null;
|
|
78
|
+
}
|
|
79
|
+
export function extractTenantId(url) {
|
|
80
|
+
const match = url.match(/\/tenants\/(Tenants-\d+)/);
|
|
81
|
+
return match ? match[1] : null;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=urlParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urlParser.js","sourceRoot":"","sources":["../../src/helpers/urlParser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Client } from "@octopusdeploy/api-client";
|
|
2
|
+
export interface CurrentUser {
|
|
3
|
+
Id: string;
|
|
4
|
+
Username: string;
|
|
5
|
+
DisplayName: string;
|
|
6
|
+
IsActive: boolean;
|
|
7
|
+
IsService: boolean;
|
|
8
|
+
EmailAddress: string;
|
|
9
|
+
CanPasswordBeEdited: boolean;
|
|
10
|
+
IsRequestor: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function getCurrentUserCached(client: Client): Promise<CurrentUser>;
|
|
13
|
+
export declare function clearUserCache(): void;
|
|
14
|
+
//# sourceMappingURL=userCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userCache.d.ts","sourceRoot":"","sources":["../../src/helpers/userCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;CACtB;AAMD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAS/E;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {} from "@octopusdeploy/api-client";
|
|
2
|
+
const CACHE_TTL = 3600000;
|
|
3
|
+
let cached;
|
|
4
|
+
export async function getCurrentUserCached(client) {
|
|
5
|
+
const now = Date.now();
|
|
6
|
+
if (cached && now - cached.timestamp < CACHE_TTL) {
|
|
7
|
+
return cached.user;
|
|
8
|
+
}
|
|
9
|
+
const user = await client.get("~/api/users/me");
|
|
10
|
+
cached = { user, timestamp: now };
|
|
11
|
+
return user;
|
|
12
|
+
}
|
|
13
|
+
export function clearUserCache() {
|
|
14
|
+
cached = undefined;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=userCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userCache.js","sourceRoot":"","sources":["../../src/helpers/userCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,MAAM,2BAA2B,CAAC;AAaxD,MAAM,SAAS,GAAG,OAAO,CAAC;AAE1B,IAAI,MAA4D,CAAC;AAEjE,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAc;IACvD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IAAI,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC;QACjD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAc,gBAAgB,CAAC,CAAC;IAC7D,MAAM,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IAClC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,GAAG,SAAS,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reject `execute` paths whose canonical form differs from what the allowlist
|
|
3
|
+
* and denylist would see, so a path like
|
|
4
|
+
*
|
|
5
|
+
* /api/spaces/Spaces-1/../../users/me/apikeys
|
|
6
|
+
*
|
|
7
|
+
* cannot pass the `/api/spaces/**` allowlist while resolving to
|
|
8
|
+
* `/api/users/me/apikeys` on the server.
|
|
9
|
+
*
|
|
10
|
+
* The validator is deliberately conservative: anything that introduces
|
|
11
|
+
* ambiguity between the literal path string and what the HTTP client / Octopus
|
|
12
|
+
* routing will see — `..` segments, encoded slashes, backslashes, query
|
|
13
|
+
* strings, fragments, double slashes — is rejected outright. We do NOT try to
|
|
14
|
+
* normalise the path; rejecting unambiguously is safer than guessing the
|
|
15
|
+
* caller's intent.
|
|
16
|
+
*
|
|
17
|
+
* Query parameters belong in the `query` argument of the execute tool, not
|
|
18
|
+
* inside the `path` string — surfacing that as a reject prompts the agent to
|
|
19
|
+
* use the right field.
|
|
20
|
+
*/
|
|
21
|
+
export type PathValidation = {
|
|
22
|
+
ok: true;
|
|
23
|
+
path: string;
|
|
24
|
+
} | {
|
|
25
|
+
ok: false;
|
|
26
|
+
reason: string;
|
|
27
|
+
};
|
|
28
|
+
export declare function validateExecutePath(raw: string): PathValidation;
|
|
29
|
+
//# sourceMappingURL=validateExecutePath.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateExecutePath.d.ts","sourceRoot":"","sources":["../../src/helpers/validateExecutePath.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CA2C/D"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reject `execute` paths whose canonical form differs from what the allowlist
|
|
3
|
+
* and denylist would see, so a path like
|
|
4
|
+
*
|
|
5
|
+
* /api/spaces/Spaces-1/../../users/me/apikeys
|
|
6
|
+
*
|
|
7
|
+
* cannot pass the `/api/spaces/**` allowlist while resolving to
|
|
8
|
+
* `/api/users/me/apikeys` on the server.
|
|
9
|
+
*
|
|
10
|
+
* The validator is deliberately conservative: anything that introduces
|
|
11
|
+
* ambiguity between the literal path string and what the HTTP client / Octopus
|
|
12
|
+
* routing will see — `..` segments, encoded slashes, backslashes, query
|
|
13
|
+
* strings, fragments, double slashes — is rejected outright. We do NOT try to
|
|
14
|
+
* normalise the path; rejecting unambiguously is safer than guessing the
|
|
15
|
+
* caller's intent.
|
|
16
|
+
*
|
|
17
|
+
* Query parameters belong in the `query` argument of the execute tool, not
|
|
18
|
+
* inside the `path` string — surfacing that as a reject prompts the agent to
|
|
19
|
+
* use the right field.
|
|
20
|
+
*/
|
|
21
|
+
export function validateExecutePath(raw) {
|
|
22
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
23
|
+
return { ok: false, reason: "Path must be a non-empty string." };
|
|
24
|
+
}
|
|
25
|
+
if (!raw.startsWith("/")) {
|
|
26
|
+
return { ok: false, reason: "Path must start with '/'." };
|
|
27
|
+
}
|
|
28
|
+
if (raw.includes("\\")) {
|
|
29
|
+
return { ok: false, reason: "Path must not contain backslashes." };
|
|
30
|
+
}
|
|
31
|
+
if (raw.includes("?")) {
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
reason: "Path must not contain a query string. Pass query parameters via the `query` argument instead.",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (raw.includes("#")) {
|
|
38
|
+
return { ok: false, reason: "Path must not contain a fragment ('#')." };
|
|
39
|
+
}
|
|
40
|
+
if (raw.includes("//")) {
|
|
41
|
+
return { ok: false, reason: "Path must not contain '//'." };
|
|
42
|
+
}
|
|
43
|
+
if (/%2f|%5c/i.test(raw)) {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
reason: "Path must not contain percent-encoded slashes ('%2F') or backslashes ('%5C').",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Reject any `..` segment — exact, leading, trailing, or surrounded by
|
|
50
|
+
// slashes. We do not attempt to resolve them.
|
|
51
|
+
const segments = raw.split("/");
|
|
52
|
+
for (const segment of segments) {
|
|
53
|
+
if (segment === "..") {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
reason: "Path must not contain '..' segments. Provide the canonical path explicitly.",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { ok: true, path: raw };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=validateExecutePath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateExecutePath.js","sourceRoot":"","sources":["../../src/helpers/validateExecutePath.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kCAAkC,EAAE,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;IAC5D,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,EAAE,CAAC;IACrE,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EACJ,+FAA+F;SAClG,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,yCAAyC,EAAE,CAAC;IAC1E,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EACJ,+EAA+E;SAClF,CAAC;IACJ,CAAC;IACD,uEAAuE;IACvE,8CAA8C;IAC9C,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EACJ,6EAA6E;aAChF,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AACjC,CAAC"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAoBA,eAAO,MAAM,cAAc,QAAsB,CAAC"}
|