Skip to content

Update changes - #773

Merged
mcowger merged 7 commits into
mainfrom
humorous-newt
Jul 31, 2026
Merged

Update changes#773
mcowger merged 7 commits into
mainfrom
humorous-newt

Conversation

@mcowger

@mcowger mcowger commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a dynamically discovered Plexus management CLI and makes the OpenAPI contract available to it at runtime.

  • Serve a public, dereferenced OpenAPI document with ETag support; document uncovered management routes and standardize paginated management responses.
  • Add @mcowger/plexus-cli / plexuscli, including OpenAPI-driven operation discovery, structured output, destructive-operation prompts, pagination, compiled binary artifacts, and npm trusted publishing from the release workflow.
  • Add CLI unit and process-level integration coverage.
  • Rename management skills to plexus-cli and plexus-rest-api, prefer the CLI when available, and expose both skill documents on the MCP page.
  • Align local dev port discovery with the active Paseo service.

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit 752e569)

🏅 Score: 95
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ No major issues detected

Comment thread packages/cli/src/cli.ts
Comment on lines +116 to +129
for (const [path, pathItem] of Object.entries(document.paths ?? {})) {
if (!ALLOWED_PATH.test(path)) continue;
for (const [method, value] of Object.entries(pathItem)) {
if (!HTTP_METHODS.has(method) || !value || typeof value !== 'object') continue;
const operation = value as OpenApiOperation;
if (isStreamOperation(operation)) continue;
candidates.push({
id: operation.operationId ?? fallbackId(method, path, operation.tags),
method,
path,
operation,
});
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Include path-item-level OpenAPI parameters when constructing an operation. OpenAPI permits shared parameters such as path IDs to be declared on the path item, and ignoring them lets calls omit required values or rejects valid --param inputs. [possible bug, importance: 6]

Suggested change
for (const [path, pathItem] of Object.entries(document.paths ?? {})) {
if (!ALLOWED_PATH.test(path)) continue;
for (const [method, value] of Object.entries(pathItem)) {
if (!HTTP_METHODS.has(method) || !value || typeof value !== 'object') continue;
const operation = value as OpenApiOperation;
if (isStreamOperation(operation)) continue;
candidates.push({
id: operation.operationId ?? fallbackId(method, path, operation.tags),
method,
path,
operation,
});
}
}
for (const [path, pathItem] of Object.entries(document.paths ?? {})) {
if (!ALLOWED_PATH.test(path)) continue;
const pathParameters = Array.isArray(pathItem.parameters)
? (pathItem.parameters as OpenApiParameter[])
: [];
for (const [method, value] of Object.entries(pathItem)) {
if (!HTTP_METHODS.has(method) || !value || typeof value !== 'object') continue;
const operation = value as OpenApiOperation;
if (isStreamOperation(operation)) continue;
const parameters = new Map(
pathParameters.map((parameter) => [`${parameter.in}:${parameter.name}`, parameter])
);
for (const parameter of operation.parameters ?? []) {
parameters.set(`${parameter.in}:${parameter.name}`, parameter);
}
candidates.push({
id: operation.operationId ?? fallbackId(method, path, operation.tags),
method,
path,
operation: { ...operation, parameters: [...parameters.values()] },
});
}
}

Comment thread packages/cli/src/cli.ts
Comment on lines +393 to +402
if (args.all) {
if (body) throw new CliError('--all cannot be used with a request body');
runtime.stdout(
formatOutput(
await fetchAllPages(baseUrl, operation, args.params, request.headers, runtime.fetch),
output
)
);
return 0;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Restrict --all to GET operations before issuing paginated requests. As written, a documented DELETE or other mutation with limit and offset can be repeatedly executed, including after just one risky-operation confirmation. [security, importance: 8]

Suggested change
if (args.all) {
if (body) throw new CliError('--all cannot be used with a request body');
runtime.stdout(
formatOutput(
await fetchAllPages(baseUrl, operation, args.params, request.headers, runtime.fetch),
output
)
);
return 0;
}
if (args.all) {
if (operation.method !== 'get') throw new CliError('--all can only be used with GET operations');
if (body) throw new CliError('--all cannot be used with a request body');
runtime.stdout(
formatOutput(
await fetchAllPages(baseUrl, operation, args.params, request.headers, runtime.fetch),
output
)
);
return 0;
}

Comment thread packages/cli/src/cli.ts Outdated
Comment on lines +278 to +281
limit ??= page.data.length;
if (!limit || page.data.length === 0) break;
allData.push(...page.data);
offset += limit;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Advance the offset by the number of records actually returned, rather than the requested limit. Servers may clamp a requested limit or return short pages, and advancing by the requested value can skip records. [possible bug, importance: 6]

Suggested change
limit ??= page.data.length;
if (!limit || page.data.length === 0) break;
allData.push(...page.data);
offset += limit;
const received = page.data.length;
limit ??= received;
if (!limit || received === 0) break;
allData.push(...page.data);
offset += received;

Comment thread packages/cli/package.json
Comment on lines +6 to +8
"bin": {
"plexuscli": "src/index.ts"
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: npm-generated command shims execute bin targets with Node, which cannot run a TypeScript entrypoint. Point this at a published JavaScript launcher that invokes Bun (or at a bundled JavaScript entrypoint), so npm install -g @mcowger/plexus-cli produces a working plexuscli command. [possible bug, importance: 7]

Suggested change
"bin": {
"plexuscli": "src/index.ts"
},
"bin": {
"plexuscli": "bin/plexuscli.js"
}

Comment thread packages/cli/src/cli.ts Outdated
Comment on lines +280 to +283
limit ??= page.data.length;
if (!limit || page.data.length === 0) break;
allData.push(...page.data);
offset += limit;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Advance offset by the number of records actually returned, not the requested limit. Servers may clamp limits or return partial pages; using the requested value can skip records during --all retrieval. [possible bug, importance: 7]

Suggested change
limit ??= page.data.length;
if (!limit || page.data.length === 0) break;
allData.push(...page.data);
offset += limit;
limit ??= page.data.length;
if (!limit || page.data.length === 0) break;
allData.push(...page.data);
offset += page.data.length;

Comment thread packages/cli/src/cli.ts
Comment on lines +122 to +129
const operation = value as OpenApiOperation;
if (isStreamOperation(operation)) continue;
candidates.push({
id: operation.operationId ?? fallbackId(method, path, operation.tags),
method,
path,
operation,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Merge path-item parameters into each discovered operation before building requests. OpenAPI permits shared path parameters at the path-item level, and ignoring them makes valid operations fail with unknown or missing parameters. [possible issue, importance: 6]

Suggested change
const operation = value as OpenApiOperation;
if (isStreamOperation(operation)) continue;
candidates.push({
id: operation.operationId ?? fallbackId(method, path, operation.tags),
method,
path,
operation,
});
const operationValue = value as OpenApiOperation;
const operation: OpenApiOperation = {
...operationValue,
parameters: [
...(((pathItem.parameters as OpenApiParameter[] | undefined) ?? [])),
...(operationValue.parameters ?? []),
],
};
if (isStreamOperation(operation)) continue;
candidates.push({
id: operation.operationId ?? fallbackId(method, path, operation.tags),
method,
path,
operation,
});

Comment thread .github/workflows/release.yml
Comment thread packages/cli/src/cli.ts
let body = args.body;
if (args.bodyFile) body = await Bun.file(args.bodyFile).text();
if (body === '-') body = await runtime.stdin();
if (!body && !runtime.isTTY) body = (await runtime.stdin()).trim() || undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Only consume implicit stdin when the discovered operation accepts a request body. As written, every non-TTY read-only call consumes stdin and can block when the parent process keeps its pipe open. [possible bug, importance: 6]

Suggested change
if (!body && !runtime.isTTY) body = (await runtime.stdin()).trim() || undefined;
if (!body && !runtime.isTTY && operation.operation.requestBody)
body = (await runtime.stdin()).trim() || undefined;

@mcowger
mcowger merged commit aa10405 into main Jul 31, 2026
4 checks passed
@mcowger
mcowger deleted the humorous-newt branch July 31, 2026 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant