toolcraft-openapi 0.0.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.
Files changed (157) hide show
  1. package/README.md +64 -0
  2. package/dist/api-command.d.ts +7 -0
  3. package/dist/api-command.js +4 -0
  4. package/dist/auth/bearer-token-auth.d.ts +8 -0
  5. package/dist/auth/bearer-token-auth.js +216 -0
  6. package/dist/auth/types.d.ts +9 -0
  7. package/dist/auth/types.js +1 -0
  8. package/dist/bin/generate.d.ts +40 -0
  9. package/dist/bin/generate.js +248 -0
  10. package/dist/define-client.d.ts +20 -0
  11. package/dist/define-client.js +148 -0
  12. package/dist/generate.d.ts +210 -0
  13. package/dist/generate.js +1131 -0
  14. package/dist/group-by-noun.d.ts +6 -0
  15. package/dist/group-by-noun.js +17 -0
  16. package/dist/http.d.ts +26 -0
  17. package/dist/http.js +123 -0
  18. package/dist/index.d.ts +12 -0
  19. package/dist/index.js +6 -0
  20. package/dist/interpreter.d.ts +6 -0
  21. package/dist/interpreter.js +289 -0
  22. package/dist/lock.d.ts +14 -0
  23. package/dist/lock.js +48 -0
  24. package/dist/naming.d.ts +24 -0
  25. package/dist/naming.js +218 -0
  26. package/dist/request-shape.d.ts +15 -0
  27. package/dist/request-shape.js +5 -0
  28. package/dist/runtime.d.ts +13 -0
  29. package/dist/runtime.js +94 -0
  30. package/dist/spec-source.d.ts +11 -0
  31. package/dist/spec-source.js +63 -0
  32. package/node_modules/@poe-code/design-system/dist/acp/components.d.ts +11 -0
  33. package/node_modules/@poe-code/design-system/dist/acp/components.js +121 -0
  34. package/node_modules/@poe-code/design-system/dist/acp/index.d.ts +3 -0
  35. package/node_modules/@poe-code/design-system/dist/acp/index.js +2 -0
  36. package/node_modules/@poe-code/design-system/dist/acp/writer.d.ts +13 -0
  37. package/node_modules/@poe-code/design-system/dist/acp/writer.js +21 -0
  38. package/node_modules/@poe-code/design-system/dist/components/command-errors.d.ts +16 -0
  39. package/node_modules/@poe-code/design-system/dist/components/command-errors.js +22 -0
  40. package/node_modules/@poe-code/design-system/dist/components/help-formatter.d.ts +20 -0
  41. package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +27 -0
  42. package/node_modules/@poe-code/design-system/dist/components/index.d.ts +10 -0
  43. package/node_modules/@poe-code/design-system/dist/components/index.js +7 -0
  44. package/node_modules/@poe-code/design-system/dist/components/logger.d.ts +11 -0
  45. package/node_modules/@poe-code/design-system/dist/components/logger.js +60 -0
  46. package/node_modules/@poe-code/design-system/dist/components/symbols.d.ts +12 -0
  47. package/node_modules/@poe-code/design-system/dist/components/symbols.js +71 -0
  48. package/node_modules/@poe-code/design-system/dist/components/table.d.ts +13 -0
  49. package/node_modules/@poe-code/design-system/dist/components/table.js +74 -0
  50. package/node_modules/@poe-code/design-system/dist/components/text.d.ts +14 -0
  51. package/node_modules/@poe-code/design-system/dist/components/text.js +104 -0
  52. package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +18 -0
  53. package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +298 -0
  54. package/node_modules/@poe-code/design-system/dist/dashboard/buffer.d.ts +25 -0
  55. package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +189 -0
  56. package/node_modules/@poe-code/design-system/dist/dashboard/components/border.d.ts +9 -0
  57. package/node_modules/@poe-code/design-system/dist/dashboard/components/border.js +123 -0
  58. package/node_modules/@poe-code/design-system/dist/dashboard/components/footer.d.ts +8 -0
  59. package/node_modules/@poe-code/design-system/dist/dashboard/components/footer.js +57 -0
  60. package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.d.ts +12 -0
  61. package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +254 -0
  62. package/node_modules/@poe-code/design-system/dist/dashboard/components/stats-pane.d.ts +7 -0
  63. package/node_modules/@poe-code/design-system/dist/dashboard/components/stats-pane.js +121 -0
  64. package/node_modules/@poe-code/design-system/dist/dashboard/dashboard.d.ts +20 -0
  65. package/node_modules/@poe-code/design-system/dist/dashboard/dashboard.js +167 -0
  66. package/node_modules/@poe-code/design-system/dist/dashboard/demo.d.ts +13 -0
  67. package/node_modules/@poe-code/design-system/dist/dashboard/demo.js +145 -0
  68. package/node_modules/@poe-code/design-system/dist/dashboard/index.d.ts +8 -0
  69. package/node_modules/@poe-code/design-system/dist/dashboard/index.js +4 -0
  70. package/node_modules/@poe-code/design-system/dist/dashboard/keymap.d.ts +3 -0
  71. package/node_modules/@poe-code/design-system/dist/dashboard/keymap.js +99 -0
  72. package/node_modules/@poe-code/design-system/dist/dashboard/layout.d.ts +25 -0
  73. package/node_modules/@poe-code/design-system/dist/dashboard/layout.js +79 -0
  74. package/node_modules/@poe-code/design-system/dist/dashboard/should-use-dashboard.d.ts +10 -0
  75. package/node_modules/@poe-code/design-system/dist/dashboard/should-use-dashboard.js +7 -0
  76. package/node_modules/@poe-code/design-system/dist/dashboard/snapshot.d.ts +10 -0
  77. package/node_modules/@poe-code/design-system/dist/dashboard/snapshot.js +68 -0
  78. package/node_modules/@poe-code/design-system/dist/dashboard/store.d.ts +8 -0
  79. package/node_modules/@poe-code/design-system/dist/dashboard/store.js +51 -0
  80. package/node_modules/@poe-code/design-system/dist/dashboard/terminal.d.ts +37 -0
  81. package/node_modules/@poe-code/design-system/dist/dashboard/terminal.js +233 -0
  82. package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +36 -0
  83. package/node_modules/@poe-code/design-system/dist/dashboard/types.js +1 -0
  84. package/node_modules/@poe-code/design-system/dist/index.d.ts +33 -0
  85. package/node_modules/@poe-code/design-system/dist/index.js +31 -0
  86. package/node_modules/@poe-code/design-system/dist/internal/output-format.d.ts +6 -0
  87. package/node_modules/@poe-code/design-system/dist/internal/output-format.js +22 -0
  88. package/node_modules/@poe-code/design-system/dist/internal/strip-ansi.d.ts +1 -0
  89. package/node_modules/@poe-code/design-system/dist/internal/strip-ansi.js +3 -0
  90. package/node_modules/@poe-code/design-system/dist/internal/theme-detect.d.ts +11 -0
  91. package/node_modules/@poe-code/design-system/dist/internal/theme-detect.js +49 -0
  92. package/node_modules/@poe-code/design-system/dist/prompts/index.d.ts +66 -0
  93. package/node_modules/@poe-code/design-system/dist/prompts/index.js +132 -0
  94. package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.d.ts +2 -0
  95. package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.js +9 -0
  96. package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.d.ts +1 -0
  97. package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +15 -0
  98. package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.d.ts +18 -0
  99. package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +101 -0
  100. package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.d.ts +1 -0
  101. package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +39 -0
  102. package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.d.ts +1 -0
  103. package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.js +16 -0
  104. package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.d.ts +6 -0
  105. package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.js +74 -0
  106. package/node_modules/@poe-code/design-system/dist/prompts/theme.d.ts +11 -0
  107. package/node_modules/@poe-code/design-system/dist/prompts/theme.js +12 -0
  108. package/node_modules/@poe-code/design-system/dist/static/index.d.ts +4 -0
  109. package/node_modules/@poe-code/design-system/dist/static/index.js +2 -0
  110. package/node_modules/@poe-code/design-system/dist/static/menu.d.ts +11 -0
  111. package/node_modules/@poe-code/design-system/dist/static/menu.js +36 -0
  112. package/node_modules/@poe-code/design-system/dist/static/spinner.d.ts +14 -0
  113. package/node_modules/@poe-code/design-system/dist/static/spinner.js +46 -0
  114. package/node_modules/@poe-code/design-system/dist/terminal-markdown/ast.d.ts +92 -0
  115. package/node_modules/@poe-code/design-system/dist/terminal-markdown/ast.js +1 -0
  116. package/node_modules/@poe-code/design-system/dist/terminal-markdown/demo-content.d.ts +2 -0
  117. package/node_modules/@poe-code/design-system/dist/terminal-markdown/demo-content.js +139 -0
  118. package/node_modules/@poe-code/design-system/dist/terminal-markdown/index.d.ts +6 -0
  119. package/node_modules/@poe-code/design-system/dist/terminal-markdown/index.js +8 -0
  120. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/block.d.ts +7 -0
  121. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/block.js +1495 -0
  122. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.d.ts +8 -0
  123. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +412 -0
  124. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/inline.d.ts +10 -0
  125. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/inline.js +1166 -0
  126. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser.d.ts +5 -0
  127. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser.js +42 -0
  128. package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.d.ts +6 -0
  129. package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +572 -0
  130. package/node_modules/@poe-code/design-system/dist/terminal-markdown/testing/theme-render-fixture.d.ts +1 -0
  131. package/node_modules/@poe-code/design-system/dist/terminal-markdown/testing/theme-render-fixture.js +27 -0
  132. package/node_modules/@poe-code/design-system/dist/tokens/colors.d.ts +35 -0
  133. package/node_modules/@poe-code/design-system/dist/tokens/colors.js +34 -0
  134. package/node_modules/@poe-code/design-system/dist/tokens/index.d.ts +4 -0
  135. package/node_modules/@poe-code/design-system/dist/tokens/index.js +4 -0
  136. package/node_modules/@poe-code/design-system/dist/tokens/spacing.d.ts +6 -0
  137. package/node_modules/@poe-code/design-system/dist/tokens/spacing.js +6 -0
  138. package/node_modules/@poe-code/design-system/dist/tokens/typography.d.ts +7 -0
  139. package/node_modules/@poe-code/design-system/dist/tokens/typography.js +8 -0
  140. package/node_modules/@poe-code/design-system/dist/tokens/widths.d.ts +5 -0
  141. package/node_modules/@poe-code/design-system/dist/tokens/widths.js +5 -0
  142. package/node_modules/@poe-code/design-system/package.json +25 -0
  143. package/node_modules/auth-store/README.md +47 -0
  144. package/node_modules/auth-store/dist/create-secret-store.d.ts +2 -0
  145. package/node_modules/auth-store/dist/create-secret-store.js +35 -0
  146. package/node_modules/auth-store/dist/encrypted-file-store.d.ts +39 -0
  147. package/node_modules/auth-store/dist/encrypted-file-store.js +156 -0
  148. package/node_modules/auth-store/dist/index.d.ts +7 -0
  149. package/node_modules/auth-store/dist/index.js +4 -0
  150. package/node_modules/auth-store/dist/keychain-store.d.ts +22 -0
  151. package/node_modules/auth-store/dist/keychain-store.js +111 -0
  152. package/node_modules/auth-store/dist/provider-store.d.ts +10 -0
  153. package/node_modules/auth-store/dist/provider-store.js +28 -0
  154. package/node_modules/auth-store/dist/types.d.ts +20 -0
  155. package/node_modules/auth-store/dist/types.js +1 -0
  156. package/node_modules/auth-store/package.json +25 -0
  157. package/package.json +48 -0
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # toolcraft-openapi
2
+
3
+ tools for agents and humans
4
+
5
+ Scaffold for OpenAPI-driven toolcraft clients.
6
+
7
+ ## Usage
8
+
9
+ ```ts
10
+ import { bearerTokenAuth, requestJson } from "agent-kit-openapi";
11
+
12
+ const auth = bearerTokenAuth({
13
+ serviceName: "internal-agent",
14
+ envVar: "INTERNAL_AGENT_TOKEN",
15
+ whoamiPath: "/whoami"
16
+ });
17
+ ```
18
+
19
+ ## Generator CLI
20
+
21
+ `agent-kit-openapi-generate` reads an OpenAPI document from disk or a URL, writes generated
22
+ command files, and stores the current spec hash in `openapi.lock`.
23
+
24
+ - `--input <path-or-url>` — OpenAPI document to read. Defaults to `openapi.json`.
25
+ - `--output <dir>` — directory for generated files. Defaults to `src/generated`.
26
+ - `--lock <path>` — lock file path. Defaults to `openapi.lock`.
27
+ - `--check` — exits non-zero when generated files or `openapi.lock` would change.
28
+
29
+ ### CI drift check
30
+
31
+ ```sh
32
+ agent-kit-openapi-generate --check
33
+ ```
34
+
35
+ ## Exports
36
+
37
+ - `bearerTokenAuth(opts)`
38
+ - `requestJson(options)`
39
+ - `HttpError`
40
+ - `TokenSource`
41
+ - `CommandContributor`
42
+ - `AuthProvider`
43
+
44
+ ## Environment variables
45
+
46
+ - `<envVar passed to bearerTokenAuth>` — bearer token override; wins over stored credentials.
47
+ - `AUTH_BACKEND` — forwarded to `auth-store` to select `file` or `keychain` storage.
48
+
49
+ ## Configuration
50
+
51
+ ### `bearerTokenAuth(opts)`
52
+
53
+ - `serviceName: string` — auth-store service name and file-store key.
54
+ - `envVar: string` — environment variable checked before stored credentials.
55
+ - `whoamiPath?: string` — optional authenticated endpoint used by `login` and `status`.
56
+ - `commandPrefix?: string` — CLI auth group name. Defaults to `auth`.
57
+
58
+ ## Auth commands
59
+
60
+ `bearerTokenAuth()` contributes a CLI-only `<commandPrefix>` group with:
61
+
62
+ - `login`
63
+ - `logout`
64
+ - `status`
@@ -0,0 +1,7 @@
1
+ import type { Command } from "agent-kit";
2
+ import { defineCommand } from "agent-kit";
3
+ import type { ObjectSchema } from "agent-kit-schema";
4
+ import type { OpenApiClientServices } from "./define-client.js";
5
+ type ApiScope = readonly ["cli", "mcp", "sdk"];
6
+ export declare function defineApiCommand<TParamsSchema extends ObjectSchema<any>>(config: Parameters<typeof defineCommand<OpenApiClientServices, string, TParamsSchema, undefined, unknown, ApiScope>>[0]): Command<OpenApiClientServices, TParamsSchema, undefined, unknown>;
7
+ export {};
@@ -0,0 +1,4 @@
1
+ import { defineCommand } from "agent-kit";
2
+ export function defineApiCommand(config) {
3
+ return defineCommand(config);
4
+ }
@@ -0,0 +1,8 @@
1
+ import type { AuthProvider } from "./types.js";
2
+ export interface BearerTokenAuthOptions {
3
+ serviceName: string;
4
+ envVar: string;
5
+ whoamiPath?: string;
6
+ commandPrefix?: string;
7
+ }
8
+ export declare function bearerTokenAuth(options: BearerTokenAuthOptions): AuthProvider;
@@ -0,0 +1,216 @@
1
+ import { defineCommand, defineGroup, S, UserError } from "agent-kit";
2
+ import { isCancel, password } from "@poe-code/design-system";
3
+ import { createSecretStore } from "auth-store";
4
+ import { requestJson } from "../http.js";
5
+ const DEFAULT_COMMAND_PREFIX = "auth";
6
+ const loginParams = S.Object({
7
+ token: S.Optional(S.String({ description: "Bearer token to store." })),
8
+ tokenStdin: S.Optional(S.Boolean({ description: "Read the token from stdin instead of prompting." })),
9
+ });
10
+ const emptyParams = S.Object({});
11
+ const KEYCHAIN_ACCOUNT = "token";
12
+ const DEFAULT_STORE_DIRECTORY = ".agent-kit-openapi";
13
+ const DEFAULT_STORE_VERSION = "v1";
14
+ export function bearerTokenAuth(options) {
15
+ const commandPrefix = options.commandPrefix ?? DEFAULT_COMMAND_PREFIX;
16
+ const { store, backend } = createSecretStore({
17
+ fileStore: {
18
+ salt: `${options.serviceName}:agent-kit-openapi:${DEFAULT_STORE_VERSION}`,
19
+ defaultDirectory: DEFAULT_STORE_DIRECTORY,
20
+ defaultFileName: `${options.serviceName}.enc`,
21
+ },
22
+ keychainStore: {
23
+ service: options.serviceName,
24
+ account: KEYCHAIN_ACCOUNT,
25
+ },
26
+ });
27
+ async function resolveToken() {
28
+ const envToken = normalizeToken(process.env[options.envVar]);
29
+ if (envToken) {
30
+ return {
31
+ token: envToken,
32
+ tokenSource: `env (${options.envVar})`,
33
+ };
34
+ }
35
+ const storedToken = normalizeToken(await store.get());
36
+ if (!storedToken) {
37
+ return null;
38
+ }
39
+ return {
40
+ token: storedToken,
41
+ tokenSource: backend,
42
+ };
43
+ }
44
+ const loginCommand = defineCommand({
45
+ name: "login",
46
+ description: "Store a bearer token for future requests.",
47
+ params: loginParams,
48
+ handler: async (ctx) => {
49
+ const token = await resolveLoginToken(ctx.params, ctx.readStdin);
50
+ const identity = await resolveIdentity(token, ctx, options.whoamiPath);
51
+ if (options.whoamiPath !== undefined && identity.isEmployee !== true) {
52
+ throw new UserError("Authenticated account is not an employee.");
53
+ }
54
+ await store.set(token);
55
+ return {
56
+ email: identity.email,
57
+ isEmployee: identity.isEmployee,
58
+ storageBackend: backend,
59
+ };
60
+ },
61
+ render: {
62
+ rich: (result, { logger }) => {
63
+ logger.success(formatLoginMessage(result));
64
+ logger.message(result.storageBackend === "keychain"
65
+ ? "Stored in macOS Keychain."
66
+ : "Stored in encrypted file store.");
67
+ },
68
+ json: (result) => result,
69
+ },
70
+ });
71
+ const logoutCommand = defineCommand({
72
+ name: "logout",
73
+ description: "Remove the stored bearer token.",
74
+ params: emptyParams,
75
+ handler: async () => {
76
+ await store.delete();
77
+ return {
78
+ storageBackend: backend,
79
+ };
80
+ },
81
+ render: {
82
+ rich: (_result, { logger }) => {
83
+ logger.success("Removed stored credential.");
84
+ },
85
+ json: (result) => result,
86
+ },
87
+ });
88
+ const statusCommand = defineCommand({
89
+ name: "status",
90
+ description: "Show where the current bearer token resolves from.",
91
+ params: emptyParams,
92
+ handler: async (ctx) => {
93
+ const resolvedToken = await resolveToken();
94
+ if (!resolvedToken) {
95
+ return { loggedIn: false };
96
+ }
97
+ const identity = await resolveIdentity(resolvedToken.token, ctx, options.whoamiPath);
98
+ return {
99
+ loggedIn: true,
100
+ tokenSource: resolvedToken.tokenSource,
101
+ email: identity.email,
102
+ isEmployee: identity.isEmployee,
103
+ };
104
+ },
105
+ render: {
106
+ rich: (result, { logger }) => {
107
+ if (!result.loggedIn) {
108
+ logger.message("Not logged in.");
109
+ return;
110
+ }
111
+ logger.success(result.email ? `Logged in as ${result.email}` : "Logged in.");
112
+ logger.message(`Token source: ${result.tokenSource}`);
113
+ },
114
+ json: (result) => result,
115
+ },
116
+ });
117
+ return {
118
+ async getToken() {
119
+ const resolvedToken = await resolveToken();
120
+ if (resolvedToken) {
121
+ return resolvedToken.token;
122
+ }
123
+ throw new UserError(`Run '${commandPrefix} login' first.`);
124
+ },
125
+ async invalidate() {
126
+ await store.delete();
127
+ },
128
+ commands: [defineGroup({
129
+ name: commandPrefix,
130
+ description: "Manage stored bearer-token authentication.",
131
+ scope: ["cli"],
132
+ children: [loginCommand, logoutCommand, statusCommand],
133
+ })],
134
+ };
135
+ }
136
+ async function resolveLoginToken(params, readStdin) {
137
+ const providedToken = normalizeToken(params.token);
138
+ if (providedToken && params.tokenStdin) {
139
+ throw new UserError("Pass either --token or --token-stdin, not both.");
140
+ }
141
+ if (providedToken) {
142
+ return providedToken;
143
+ }
144
+ if (params.tokenStdin) {
145
+ const stdinToken = normalizeToken(await (readStdin?.() ?? readAllFromStdin()));
146
+ if (!stdinToken) {
147
+ throw new UserError("Received an empty token from stdin.");
148
+ }
149
+ return stdinToken;
150
+ }
151
+ const promptedToken = await password({
152
+ message: "Paste your API key:",
153
+ });
154
+ if (isCancel(promptedToken)) {
155
+ throw new UserError("Authentication cancelled.");
156
+ }
157
+ const normalizedPromptedToken = normalizeToken(promptedToken);
158
+ if (!normalizedPromptedToken) {
159
+ throw new UserError("Token cannot be empty.");
160
+ }
161
+ return normalizedPromptedToken;
162
+ }
163
+ async function resolveIdentity(token, services, whoamiPath) {
164
+ if (whoamiPath === undefined) {
165
+ return {};
166
+ }
167
+ if (!services.baseUrl) {
168
+ throw new UserError("Auth verification requires a baseUrl service.");
169
+ }
170
+ const response = await requestJson({
171
+ baseUrl: services.baseUrl,
172
+ path: whoamiPath,
173
+ method: "GET",
174
+ auth: "required",
175
+ tokenSource: {
176
+ getToken: async () => token,
177
+ },
178
+ fetch: services.fetch,
179
+ });
180
+ return parseIdentity(response);
181
+ }
182
+ function parseIdentity(response) {
183
+ if (!isRecord(response)) {
184
+ return {};
185
+ }
186
+ return {
187
+ email: typeof response.email === "string" ? response.email : undefined,
188
+ isEmployee: typeof response.is_employee === "boolean" ? response.is_employee : undefined,
189
+ };
190
+ }
191
+ async function readAllFromStdin() {
192
+ const chunks = [];
193
+ for await (const chunk of process.stdin) {
194
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
195
+ }
196
+ return Buffer.concat(chunks).toString("utf8");
197
+ }
198
+ function normalizeToken(value) {
199
+ if (typeof value !== "string") {
200
+ return null;
201
+ }
202
+ const trimmed = value.trim();
203
+ return trimmed.length > 0 ? trimmed : null;
204
+ }
205
+ function formatLoginMessage(result) {
206
+ if (!result.email) {
207
+ return "Authenticated.";
208
+ }
209
+ if (result.isEmployee === true) {
210
+ return `Authenticated as ${result.email} (employee confirmed).`;
211
+ }
212
+ return `Authenticated as ${result.email}.`;
213
+ }
214
+ function isRecord(value) {
215
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
216
+ }
@@ -0,0 +1,9 @@
1
+ import type { CommandNode } from "agent-kit";
2
+ export interface TokenSource {
3
+ getToken(): Promise<string>;
4
+ invalidate?(): Promise<void>;
5
+ }
6
+ export interface CommandContributor {
7
+ commands: CommandNode<any>[];
8
+ }
9
+ export type AuthProvider = TokenSource & CommandContributor;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ interface GenerateCliFileSystem {
3
+ mkdir(directoryPath: string, options?: {
4
+ recursive?: boolean;
5
+ }): Promise<unknown>;
6
+ readFile(filePath: string, encoding: BufferEncoding): Promise<string>;
7
+ readdir(directoryPath: string): Promise<string[]>;
8
+ rm(targetPath: string, options?: {
9
+ force?: boolean;
10
+ }): Promise<void>;
11
+ stat(targetPath: string): Promise<{
12
+ isDirectory(): boolean;
13
+ }>;
14
+ writeFile(filePath: string, contents: string, encoding: BufferEncoding): Promise<void>;
15
+ }
16
+ interface GenerateCliWriter {
17
+ write(chunk: string | Uint8Array): boolean;
18
+ }
19
+ interface GenerateCliServices {
20
+ cwd: string;
21
+ fetch: typeof globalThis.fetch;
22
+ fs: GenerateCliFileSystem;
23
+ stderr: GenerateCliWriter;
24
+ stdout: GenerateCliWriter;
25
+ }
26
+ interface GenerateCliOptions {
27
+ check: boolean;
28
+ input: string;
29
+ lockPath: string;
30
+ outputDir: string;
31
+ }
32
+ interface SyncGeneratedClientResult {
33
+ deletedFileCount: number;
34
+ drifted: boolean;
35
+ specSha: string;
36
+ updatedFileCount: number;
37
+ }
38
+ export declare function runGenerateCli(argv?: string[], services?: GenerateCliServices): Promise<number>;
39
+ export declare function syncGeneratedClient(options: GenerateCliOptions, services: Pick<GenerateCliServices, "cwd" | "fetch" | "fs">): Promise<SyncGeneratedClientResult>;
40
+ export {};
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from "node:crypto";
3
+ import fs from "node:fs/promises";
4
+ import { realpathSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { UserError } from "agent-kit";
8
+ import { generate } from "../generate.js";
9
+ import { readOpenApiLock, writeOpenApiLock } from "../lock.js";
10
+ import { parseOpenApiDocument, readOpenApiSourceText } from "../spec-source.js";
11
+ const DEFAULT_OPTIONS = {
12
+ check: false,
13
+ input: "openapi.json",
14
+ lockPath: "openapi.lock",
15
+ outputDir: "src/generated"
16
+ };
17
+ const HELP_TEXT = `Usage: agent-kit-openapi-generate [options]
18
+
19
+ Options:
20
+ --input <path-or-url> OpenAPI document to read (default: openapi.json)
21
+ --output <dir> Directory for generated command files (default: src/generated)
22
+ --lock <path> Lock file path (default: openapi.lock)
23
+ --check Exit non-zero if generated output or lock file would change
24
+ -h, --help Show this help text
25
+ `;
26
+ export async function runGenerateCli(argv = process.argv, services = {
27
+ cwd: process.cwd(),
28
+ fetch: globalThis.fetch,
29
+ fs,
30
+ stderr: process.stderr,
31
+ stdout: process.stdout
32
+ }) {
33
+ try {
34
+ const parsed = parseGenerateCliArgs(argv.slice(2));
35
+ if (parsed === "help") {
36
+ services.stdout.write(HELP_TEXT);
37
+ return 0;
38
+ }
39
+ const result = await syncGeneratedClient(parsed, services);
40
+ if (parsed.check) {
41
+ if (result.drifted) {
42
+ services.stderr.write(`OpenAPI output is out of date for ${parsed.outputDir}. Run the generator without --check to update it.\n`);
43
+ return 1;
44
+ }
45
+ services.stdout.write(`OpenAPI output is up to date (${result.specSha}).\n`);
46
+ return 0;
47
+ }
48
+ if (result.drifted) {
49
+ services.stdout.write(`Updated OpenAPI output (${result.updatedFileCount} written, ${result.deletedFileCount} deleted).\n`);
50
+ return 0;
51
+ }
52
+ services.stdout.write(`OpenAPI output is up to date (${result.specSha}).\n`);
53
+ return 0;
54
+ }
55
+ catch (error) {
56
+ if (error instanceof UserError) {
57
+ services.stderr.write(`${error.message}\n`);
58
+ return 1;
59
+ }
60
+ throw error;
61
+ }
62
+ }
63
+ export async function syncGeneratedClient(options, services) {
64
+ const sourceText = await readOpenApiSourceText(options.input, services);
65
+ const specSha = createSpecSha(sourceText);
66
+ const document = parseOpenApiDocument(sourceText, options.input);
67
+ const generatedFiles = generate(document, { specSha });
68
+ const outputDir = path.resolve(services.cwd, options.outputDir);
69
+ const lockPath = path.resolve(services.cwd, options.lockPath);
70
+ const currentLock = await readOpenApiLock(services.fs, lockPath);
71
+ const currentFiles = await readGeneratedFiles(services.fs, outputDir);
72
+ const desiredFiles = new Map([
73
+ ...generatedFiles.map((file) => [path.resolve(outputDir, file.path), file.contents]),
74
+ ...createDownloadedSpecFiles(options.input, sourceText).map((file) => [path.resolve(outputDir, file.path), file.contents])
75
+ ]);
76
+ const updatedFiles = collectUpdatedFiles(currentFiles, desiredFiles);
77
+ const deletedFiles = collectDeletedFiles(currentFiles, desiredFiles);
78
+ const drifted = currentLock?.specSha !== specSha || updatedFiles.length > 0 || deletedFiles.length > 0;
79
+ if (!options.check && drifted) {
80
+ await writeGeneratedFiles(services.fs, updatedFiles);
81
+ await deleteGeneratedFiles(services.fs, deletedFiles);
82
+ await writeOpenApiLock(services.fs, lockPath, { specSha });
83
+ }
84
+ return {
85
+ drifted,
86
+ specSha,
87
+ updatedFileCount: updatedFiles.length,
88
+ deletedFileCount: deletedFiles.length
89
+ };
90
+ }
91
+ function parseGenerateCliArgs(argv) {
92
+ const options = { ...DEFAULT_OPTIONS };
93
+ for (let index = 0; index < argv.length; index += 1) {
94
+ const argument = argv[index] ?? "";
95
+ if (argument === "-h" || argument === "--help") {
96
+ return "help";
97
+ }
98
+ if (argument === "--check") {
99
+ options.check = true;
100
+ continue;
101
+ }
102
+ if (argument === "--input" || argument === "--output" || argument === "--lock") {
103
+ const value = argv[index + 1];
104
+ if (value === undefined) {
105
+ throw new UserError(`Missing value for ${JSON.stringify(argument)}.`);
106
+ }
107
+ assignOptionValue(options, argument, value);
108
+ index += 1;
109
+ continue;
110
+ }
111
+ if (argument.startsWith("--input=")) {
112
+ assignOptionValue(options, "--input", argument.slice("--input=".length));
113
+ continue;
114
+ }
115
+ if (argument.startsWith("--output=")) {
116
+ assignOptionValue(options, "--output", argument.slice("--output=".length));
117
+ continue;
118
+ }
119
+ if (argument.startsWith("--lock=")) {
120
+ assignOptionValue(options, "--lock", argument.slice("--lock=".length));
121
+ continue;
122
+ }
123
+ throw new UserError(`Unknown argument ${JSON.stringify(argument)}.`);
124
+ }
125
+ return options;
126
+ }
127
+ function assignOptionValue(options, argument, value) {
128
+ if (value.length === 0) {
129
+ throw new UserError(`Missing value for ${JSON.stringify(argument)}.`);
130
+ }
131
+ if (argument === "--input") {
132
+ options.input = value;
133
+ return;
134
+ }
135
+ if (argument === "--output") {
136
+ options.outputDir = value;
137
+ return;
138
+ }
139
+ options.lockPath = value;
140
+ }
141
+ function createSpecSha(sourceText) {
142
+ return `sha256:${createHash("sha256").update(sourceText).digest("hex")}`;
143
+ }
144
+ function createDownloadedSpecFiles(input, sourceText) {
145
+ const inputUrl = tryParseUrl(input);
146
+ if (inputUrl === null || (inputUrl.protocol !== "http:" && inputUrl.protocol !== "https:")) {
147
+ return [];
148
+ }
149
+ return [
150
+ {
151
+ path: getDownloadedSpecFileName(inputUrl),
152
+ contents: sourceText
153
+ }
154
+ ];
155
+ }
156
+ function getDownloadedSpecFileName(inputUrl) {
157
+ const basename = path.posix.basename(inputUrl.pathname);
158
+ return basename.length > 0 ? basename : "openapi.json";
159
+ }
160
+ function tryParseUrl(input) {
161
+ if (input instanceof URL) {
162
+ return input;
163
+ }
164
+ try {
165
+ return new URL(input);
166
+ }
167
+ catch {
168
+ return null;
169
+ }
170
+ }
171
+ async function readGeneratedFiles(fs, directoryPath) {
172
+ const files = new Map();
173
+ try {
174
+ const entries = await fs.readdir(directoryPath);
175
+ for (const entry of entries) {
176
+ const entryPath = path.resolve(directoryPath, entry);
177
+ const stats = await fs.stat(entryPath);
178
+ if (stats.isDirectory()) {
179
+ for (const [nestedPath, nestedContents] of await readGeneratedFiles(fs, entryPath)) {
180
+ files.set(nestedPath, nestedContents);
181
+ }
182
+ continue;
183
+ }
184
+ files.set(entryPath, await fs.readFile(entryPath, "utf8"));
185
+ }
186
+ }
187
+ catch (error) {
188
+ if (!isNotFoundError(error)) {
189
+ throw error;
190
+ }
191
+ }
192
+ return files;
193
+ }
194
+ function collectUpdatedFiles(currentFiles, desiredFiles) {
195
+ const updatedFiles = [];
196
+ for (const [filePath, contents] of desiredFiles) {
197
+ if (currentFiles.get(filePath) === contents) {
198
+ continue;
199
+ }
200
+ updatedFiles.push({ path: filePath, contents });
201
+ }
202
+ return updatedFiles;
203
+ }
204
+ function collectDeletedFiles(currentFiles, desiredFiles) {
205
+ const deletedFiles = [];
206
+ for (const filePath of currentFiles.keys()) {
207
+ if (desiredFiles.has(filePath)) {
208
+ continue;
209
+ }
210
+ deletedFiles.push(filePath);
211
+ }
212
+ return deletedFiles;
213
+ }
214
+ async function writeGeneratedFiles(fs, filesToWrite) {
215
+ for (const file of filesToWrite) {
216
+ await fs.mkdir(path.dirname(file.path), { recursive: true });
217
+ await fs.writeFile(file.path, file.contents, "utf8");
218
+ }
219
+ }
220
+ async function deleteGeneratedFiles(fs, filePaths) {
221
+ for (const filePath of filePaths) {
222
+ await fs.rm(filePath, { force: true });
223
+ }
224
+ }
225
+ function isNotFoundError(error) {
226
+ return (typeof error === "object" &&
227
+ error !== null &&
228
+ "code" in error &&
229
+ error.code === "ENOENT");
230
+ }
231
+ function isDirectExecution(moduleUrl, argv) {
232
+ const entryPoint = argv[1];
233
+ if (entryPoint === undefined) {
234
+ return false;
235
+ }
236
+ try {
237
+ return path.resolve(fileURLToPath(moduleUrl)) === realpathSync(path.resolve(entryPoint));
238
+ }
239
+ catch {
240
+ return false;
241
+ }
242
+ }
243
+ if (isDirectExecution(import.meta.url, process.argv)) {
244
+ const exitCode = await runGenerateCli();
245
+ if (exitCode !== 0) {
246
+ process.exit(exitCode);
247
+ }
248
+ }
@@ -0,0 +1,20 @@
1
+ import type { CommandNode, Group } from "agent-kit";
2
+ import type { AuthProvider, TokenSource } from "./auth/types.js";
3
+ export interface OpenApiClientServices {
4
+ baseUrl: string;
5
+ tokenSource: TokenSource;
6
+ }
7
+ export interface DefineClientOptions<TServices extends object = Record<string, never>> {
8
+ name: string;
9
+ baseUrl: string;
10
+ auth: AuthProvider;
11
+ commands: CommandNode<OpenApiClientServices & TServices>[];
12
+ handwrittenCommands?: CommandNode<OpenApiClientServices & TServices>[];
13
+ }
14
+ export interface DefinedClient<TServices extends object = Record<string, never>> {
15
+ name: string;
16
+ mcpPrefix: string;
17
+ root: Group<OpenApiClientServices & TServices>;
18
+ services: OpenApiClientServices;
19
+ }
20
+ export declare function defineClient<TServices extends object = Record<string, never>>(options: DefineClientOptions<TServices>): DefinedClient<TServices>;