@vucinatim/agentic-devtools 0.1.0 → 0.1.1

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 CHANGED
@@ -23,6 +23,12 @@ npx -y @vucinatim/agentic-devtools connect namecheap
23
23
  npx -y @vucinatim/agentic-devtools connect npm
24
24
  ```
25
25
 
26
+ npm publishing setup can also be driven through the package:
27
+
28
+ ```bash
29
+ npx -y @vucinatim/agentic-devtools setup-publishing npm
30
+ ```
31
+
26
32
  Start here:
27
33
 
28
34
  - [docs/architecture.md](docs/architecture.md)
@@ -118,7 +124,9 @@ npm:
118
124
  ```
119
125
 
120
126
  Use `connect npm` for guided token setup. For real package releases, prefer
121
- GitHub Actions Trusted Publishing over local write tokens.
127
+ GitHub Actions Trusted Publishing over local write tokens. Use
128
+ `setup-publishing npm` to run npm's official Trusted Publishing setup command
129
+ through the package.
122
130
 
123
131
  ### Global CLI
124
132
 
@@ -23,6 +23,7 @@ publish dry-run.
23
23
 
24
24
  - `getNpmAuthStatus`
25
25
  - `connectNpm`
26
+ - `setupNpmGitHubTrustedPublisher`
26
27
  - `disconnectNpm`
27
28
  - `testNpmConnection`
28
29
  - `getNpmPackageInfo`
@@ -155,7 +155,24 @@ After npm Trusted Publishing is configured for this exact repo and workflow,
155
155
 
156
156
  ## npm Trusted Publisher Setup
157
157
 
158
- In npm package settings, configure a trusted publisher for:
158
+ Use the package-owned setup flow:
159
+
160
+ ```bash
161
+ npx -y @vucinatim/agentic-devtools setup-publishing npm
162
+ ```
163
+
164
+ The flow runs npm's official trust command, using the saved npm token from
165
+ `connect npm` when one is available:
166
+
167
+ ```bash
168
+ npx -y npm@^11.10.0 login --auth-type=web --registry https://registry.npmjs.org
169
+ npx -y npm@^11.10.0 trust github @vucinatim/agentic-devtools --repo vucinatim/agentic-devtools --file publish.yml --yes
170
+ ```
171
+
172
+ This keeps the setup aligned with npm's beta Trusted Publishing flow and lets
173
+ npm handle web/security-key two-factor authentication when required.
174
+
175
+ For this repository it configures:
159
176
 
160
177
  - provider: GitHub Actions
161
178
  - organization/user: `vucinatim`
package/docs/usage.md CHANGED
@@ -13,6 +13,7 @@ in MCP host config:
13
13
  npx -y @vucinatim/agentic-devtools connect railway
14
14
  npx -y @vucinatim/agentic-devtools connect namecheap
15
15
  npx -y @vucinatim/agentic-devtools connect npm
16
+ npx -y @vucinatim/agentic-devtools setup-publishing npm
16
17
  ```
17
18
 
18
19
  ## Primary: MCP Host With `npx`
@@ -73,7 +74,9 @@ npm:
73
74
  ```
74
75
 
75
76
  Use `connect npm` for guided token setup when package inspection or token
76
- operations require authentication.
77
+ operations require authentication. Use `setup-publishing npm` when the package
78
+ needs to attach GitHub Actions Trusted Publishing through npm's official trust
79
+ setup flow.
77
80
 
78
81
  ## Secondary: Global CLI
79
82
 
@@ -125,6 +128,7 @@ Namecheap supports:
125
128
  npm supports:
126
129
 
127
130
  - guided local setup through `agentic-devtools connect npm`
131
+ - guided GitHub Actions Trusted Publishing setup through `agentic-devtools setup-publishing npm`
128
132
  - `NPM_TOKEN`
129
133
  - `NODE_AUTH_TOKEN`
130
134
  - `.npmrc` auth token resolution
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vucinatim/agentic-devtools",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "description": "MCP-first devtools for AI agents.",
6
6
  "type": "module",
package/src/cli.mjs CHANGED
@@ -7,6 +7,7 @@ const usage = () => `Usage:
7
7
  agentic-devtools tools
8
8
  agentic-devtools mcp <namecheap|railway|npm>
9
9
  agentic-devtools connect <namecheap|railway|npm>
10
+ agentic-devtools setup-publishing npm
10
11
  agentic-devtools disconnect <namecheap|railway|npm>
11
12
  agentic-devtools auth-status <namecheap|railway|npm>
12
13
  agentic-devtools test-connection <namecheap|railway|npm>
@@ -90,6 +91,33 @@ if (args[0] === "connect") {
90
91
  throw new Error("connect expects one of: namecheap, railway, npm");
91
92
  }
92
93
 
94
+ if (args[0] === "setup-publishing") {
95
+ const toolName = args[1];
96
+ if (toolName === "npm") {
97
+ const { runNpmTrustGithubSetup } = await import(
98
+ "./tools/npm/trust-cli.mjs"
99
+ );
100
+ process.stderr.write(
101
+ "Running npm's official GitHub Trusted Publishing setup flow...\n",
102
+ );
103
+ const result = await runNpmTrustGithubSetup({
104
+ stdio: "inherit",
105
+ loginFirst: true,
106
+ });
107
+ if (!result.ok) {
108
+ process.exit(result.status || 1);
109
+ }
110
+ printJson({
111
+ ok: true,
112
+ command: result.command,
113
+ args: result.args,
114
+ tokenSource: result.tokenSource,
115
+ });
116
+ process.exit(0);
117
+ }
118
+ throw new Error("setup-publishing expects: npm");
119
+ }
120
+
93
121
  if (args[0] === "disconnect") {
94
122
  const toolName = args[1];
95
123
  if (toolName === "namecheap") {
package/src/index.mjs CHANGED
@@ -44,4 +44,9 @@ export {
44
44
  encodePackageName,
45
45
  NpmRegistryError,
46
46
  } from "./tools/npm/client.mjs";
47
+ export {
48
+ buildNpmWebLoginArgs,
49
+ buildNpmTrustGithubArgs,
50
+ runNpmTrustGithubSetup,
51
+ } from "./tools/npm/trust-cli.mjs";
47
52
  export { getTool, listTools, tools } from "./core/tool-registry.mjs";
@@ -10,6 +10,7 @@ import {
10
10
  runNpmBrowserAuthFlow,
11
11
  } from "./auth.mjs";
12
12
  import { createNpmClient } from "./client.mjs";
13
+ import { runNpmTrustGithubSetup } from "./trust-cli.mjs";
13
14
 
14
15
  const HELP_TEXT = `Usage: agentic-devtools mcp npm
15
16
 
@@ -21,6 +22,12 @@ Optional environment variables:
21
22
  NPM_CONFIG_REGISTRY
22
23
  AGENTIC_DEVTOOLS_NPM_AUTH_CONFIG_PATH
23
24
 
25
+ Standalone flags:
26
+ --connect
27
+ --setup-publishing
28
+ --auth-status
29
+ --test-connection
30
+
24
31
  Prefer GitHub Actions Trusted Publishing for real package publishing. Local publishing is supported but explicit confirmation is required.
25
32
  `;
26
33
 
@@ -75,6 +82,23 @@ const createServer = () => {
75
82
  }),
76
83
  );
77
84
 
85
+ server.registerTool(
86
+ "setupNpmGitHubTrustedPublisher",
87
+ {
88
+ description:
89
+ "Run npm's official GitHub Actions Trusted Publisher setup flow. Uses npm@^11.10.0 through npx so security-key 2FA can be handled by npm.",
90
+ inputSchema: {
91
+ packageName: packageNameSchema.default("@vucinatim/agentic-devtools"),
92
+ repository: z.string().min(1).default("vucinatim/agentic-devtools"),
93
+ workflowFile: z.string().min(1).default("publish.yml"),
94
+ environment: z.string().optional(),
95
+ loginFirst: z.boolean().default(true),
96
+ },
97
+ },
98
+ async (args) =>
99
+ createToolResult(await runNpmTrustGithubSetup(args ?? {})),
100
+ );
101
+
78
102
  server.registerTool(
79
103
  "disconnectNpm",
80
104
  {
@@ -320,6 +344,32 @@ if (argv.includes("--connect")) {
320
344
  process.exit(0);
321
345
  }
322
346
 
347
+ if (argv.includes("--setup-publishing")) {
348
+ process.stdout.write(
349
+ "Running npm's official GitHub Trusted Publishing setup flow...\n",
350
+ );
351
+ const result = await runNpmTrustGithubSetup({
352
+ stdio: "inherit",
353
+ loginFirst: true,
354
+ });
355
+ if (!result.ok) {
356
+ process.exit(result.status || 1);
357
+ }
358
+ process.stdout.write(
359
+ `${JSON.stringify(
360
+ {
361
+ ok: true,
362
+ command: result.command,
363
+ args: result.args,
364
+ tokenSource: result.tokenSource,
365
+ },
366
+ null,
367
+ 2,
368
+ )}\n`,
369
+ );
370
+ process.exit(0);
371
+ }
372
+
323
373
  if (argv.includes("--test-connection")) {
324
374
  const client = createNpmClient();
325
375
  process.stdout.write(
@@ -0,0 +1,144 @@
1
+ import { spawn } from "node:child_process";
2
+ import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { DEFAULT_NPM_REGISTRY, resolveNpmAuthConfig } from "./auth.mjs";
6
+
7
+ export const DEFAULT_NPM_TRUST_CLI_SPEC = "npm@^11.10.0";
8
+
9
+ const normalizeRegistry = (registry = DEFAULT_NPM_REGISTRY) =>
10
+ String(registry || DEFAULT_NPM_REGISTRY).replace(/\/+$/, "");
11
+
12
+ export const buildNpmTrustGithubArgs = ({
13
+ packageName = "@vucinatim/agentic-devtools",
14
+ repository = "vucinatim/agentic-devtools",
15
+ workflowFile = "publish.yml",
16
+ environment = "",
17
+ yes = true,
18
+ } = {}) => {
19
+ const args = [
20
+ DEFAULT_NPM_TRUST_CLI_SPEC,
21
+ "trust",
22
+ "github",
23
+ String(packageName).trim(),
24
+ "--repo",
25
+ String(repository).trim(),
26
+ "--file",
27
+ String(workflowFile).trim(),
28
+ ];
29
+
30
+ if (environment) {
31
+ args.push("--env", String(environment).trim());
32
+ }
33
+ if (yes) {
34
+ args.push("--yes");
35
+ }
36
+
37
+ return args;
38
+ };
39
+
40
+ export const buildNpmWebLoginArgs = () => [
41
+ DEFAULT_NPM_TRUST_CLI_SPEC,
42
+ "login",
43
+ "--auth-type=web",
44
+ "--registry",
45
+ DEFAULT_NPM_REGISTRY,
46
+ ];
47
+
48
+ export const runNpmTrustGithubSetup = async ({
49
+ env = process.env,
50
+ stdio = "pipe",
51
+ loginFirst = false,
52
+ ...options
53
+ } = {}) => {
54
+ const auth = resolveNpmAuthConfig(env);
55
+ const registry = normalizeRegistry(auth.registry);
56
+ const tempDir = await mkdtemp(path.join(os.tmpdir(), "agentic-npm-trust-"));
57
+ const userconfigPath = path.join(tempDir, ".npmrc");
58
+
59
+ if (auth.token && !loginFirst) {
60
+ const registryUrl = new URL(registry);
61
+ await writeFile(
62
+ userconfigPath,
63
+ `registry=${registry}/\n//${registryUrl.host}/:_authToken=${auth.token}\n`,
64
+ );
65
+ await chmod(userconfigPath, 0o600).catch(() => {});
66
+ }
67
+
68
+ const commandEnv = {
69
+ ...process.env,
70
+ ...env,
71
+ NPM_CONFIG_REGISTRY: registry,
72
+ ...(auth.token && !loginFirst ? { NPM_CONFIG_USERCONFIG: userconfigPath } : {}),
73
+ };
74
+
75
+ try {
76
+ if (loginFirst) {
77
+ const loginArgs = ["-y", ...buildNpmWebLoginArgs()];
78
+ const loginResult = await spawnCommand("npx", loginArgs, {
79
+ env: commandEnv,
80
+ stdio,
81
+ });
82
+ if (loginResult.status !== 0) {
83
+ return {
84
+ ok: false,
85
+ status: loginResult.status,
86
+ command: "npx",
87
+ args: loginArgs,
88
+ step: "login",
89
+ tokenSource: auth.source,
90
+ stdout: loginResult.stdout,
91
+ stderr: loginResult.stderr,
92
+ };
93
+ }
94
+ }
95
+
96
+ const args = ["-y", ...buildNpmTrustGithubArgs(options)];
97
+ const result = await spawnCommand("npx", args, {
98
+ env: commandEnv,
99
+ stdio,
100
+ });
101
+
102
+ return {
103
+ ok: result.status === 0,
104
+ status: result.status,
105
+ command: "npx",
106
+ args,
107
+ step: "trust",
108
+ tokenSource: auth.source,
109
+ stdout: result.stdout,
110
+ stderr: result.stderr,
111
+ };
112
+ } finally {
113
+ await rm(tempDir, { recursive: true, force: true });
114
+ }
115
+ };
116
+
117
+ const spawnCommand = (command, args, { env, stdio }) =>
118
+ new Promise((resolve, reject) => {
119
+ const shouldCapture = stdio === "pipe";
120
+ let stdout = "";
121
+ let stderr = "";
122
+ const child = spawn(command, args, {
123
+ env,
124
+ stdio,
125
+ });
126
+
127
+ if (shouldCapture) {
128
+ child.stdout?.on("data", (chunk) => {
129
+ stdout += chunk;
130
+ });
131
+ child.stderr?.on("data", (chunk) => {
132
+ stderr += chunk;
133
+ });
134
+ }
135
+
136
+ child.on("error", reject);
137
+ child.on("close", (status) => {
138
+ resolve({
139
+ status: status ?? 1,
140
+ stdout,
141
+ stderr,
142
+ });
143
+ });
144
+ });