@agility/create-next-app 1.0.0-beta.2

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 (213) hide show
  1. package/.claude/settings.json +7 -0
  2. package/.claude/settings.local.json +24 -0
  3. package/FEATURE_ROADMAP.md +343 -0
  4. package/README.md +205 -0
  5. package/TESTING.md +131 -0
  6. package/bin/create-agility-app.js +48 -0
  7. package/dist/agility/api-keys/generateApiKeys.d.ts +9 -0
  8. package/dist/agility/api-keys/generateApiKeys.d.ts.map +1 -0
  9. package/dist/agility/api-keys/generateApiKeys.js +99 -0
  10. package/dist/agility/api-keys/generateApiKeys.js.map +1 -0
  11. package/dist/agility/api-keys/getApiKeys.d.ts +9 -0
  12. package/dist/agility/api-keys/getApiKeys.d.ts.map +1 -0
  13. package/dist/agility/api-keys/getApiKeys.js +14 -0
  14. package/dist/agility/api-keys/getApiKeys.js.map +1 -0
  15. package/dist/agility/index.d.ts +3 -0
  16. package/dist/agility/index.d.ts.map +1 -0
  17. package/dist/agility/index.js +8 -0
  18. package/dist/agility/index.js.map +1 -0
  19. package/dist/agility/instance/createNewInstance.d.ts +8 -0
  20. package/dist/agility/instance/createNewInstance.d.ts.map +1 -0
  21. package/dist/agility/instance/createNewInstance.js +65 -0
  22. package/dist/agility/instance/createNewInstance.js.map +1 -0
  23. package/dist/agility/instance/getAvailableInstances.d.ts +8 -0
  24. package/dist/agility/instance/getAvailableInstances.d.ts.map +1 -0
  25. package/dist/agility/instance/getAvailableInstances.js +43 -0
  26. package/dist/agility/instance/getAvailableInstances.js.map +1 -0
  27. package/dist/agility/instance/manageInstance.d.ts +9 -0
  28. package/dist/agility/instance/manageInstance.d.ts.map +1 -0
  29. package/dist/agility/instance/manageInstance.js +82 -0
  30. package/dist/agility/instance/manageInstance.js.map +1 -0
  31. package/dist/agility/utils/getMgmtAPIUrl.d.ts +20 -0
  32. package/dist/agility/utils/getMgmtAPIUrl.d.ts.map +1 -0
  33. package/dist/agility/utils/getMgmtAPIUrl.js +61 -0
  34. package/dist/agility/utils/getMgmtAPIUrl.js.map +1 -0
  35. package/dist/auth/api-key/authenticateWithApiKey.d.ts +6 -0
  36. package/dist/auth/api-key/authenticateWithApiKey.d.ts.map +1 -0
  37. package/dist/auth/api-key/authenticateWithApiKey.js +28 -0
  38. package/dist/auth/api-key/authenticateWithApiKey.js.map +1 -0
  39. package/dist/auth/index.d.ts +3 -0
  40. package/dist/auth/index.d.ts.map +1 -0
  41. package/dist/auth/index.js +8 -0
  42. package/dist/auth/index.js.map +1 -0
  43. package/dist/auth/oauth/authenticate.d.ts +6 -0
  44. package/dist/auth/oauth/authenticate.d.ts.map +1 -0
  45. package/dist/auth/oauth/authenticate.js +162 -0
  46. package/dist/auth/oauth/authenticate.js.map +1 -0
  47. package/dist/auth/oauth/constants.d.ts +5 -0
  48. package/dist/auth/oauth/constants.d.ts.map +1 -0
  49. package/dist/auth/oauth/constants.js +9 -0
  50. package/dist/auth/oauth/constants.js.map +1 -0
  51. package/dist/auth/oauth/exchangeCodeForToken.d.ts +7 -0
  52. package/dist/auth/oauth/exchangeCodeForToken.d.ts.map +1 -0
  53. package/dist/auth/oauth/exchangeCodeForToken.js +39 -0
  54. package/dist/auth/oauth/exchangeCodeForToken.js.map +1 -0
  55. package/dist/cli/index.d.ts +3 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +290 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/promptForMissingOptions.d.ts +8 -0
  60. package/dist/cli/promptForMissingOptions.d.ts.map +1 -0
  61. package/dist/cli/promptForMissingOptions.js +92 -0
  62. package/dist/cli/promptForMissingOptions.js.map +1 -0
  63. package/dist/config/env/createEnvFile.d.ts +6 -0
  64. package/dist/config/env/createEnvFile.d.ts.map +1 -0
  65. package/dist/config/env/createEnvFile.js +31 -0
  66. package/dist/config/env/createEnvFile.js.map +1 -0
  67. package/dist/config/index.d.ts +2 -0
  68. package/dist/config/index.d.ts.map +1 -0
  69. package/dist/config/index.js +6 -0
  70. package/dist/config/index.js.map +1 -0
  71. package/dist/config/mcp/createMcpConfig.d.ts +5 -0
  72. package/dist/config/mcp/createMcpConfig.d.ts.map +1 -0
  73. package/dist/config/mcp/createMcpConfig.js +32 -0
  74. package/dist/config/mcp/createMcpConfig.js.map +1 -0
  75. package/dist/config/packages/installAgilityPackages.d.ts +6 -0
  76. package/dist/config/packages/installAgilityPackages.d.ts.map +1 -0
  77. package/dist/config/packages/installAgilityPackages.js +61 -0
  78. package/dist/config/packages/installAgilityPackages.js.map +1 -0
  79. package/dist/config/setupProject.d.ts +8 -0
  80. package/dist/config/setupProject.d.ts.map +1 -0
  81. package/dist/config/setupProject.js +32 -0
  82. package/dist/config/setupProject.js.map +1 -0
  83. package/dist/create-next-app/createNextApp.d.ts +9 -0
  84. package/dist/create-next-app/createNextApp.d.ts.map +1 -0
  85. package/dist/create-next-app/createNextApp.js +83 -0
  86. package/dist/create-next-app/createNextApp.js.map +1 -0
  87. package/dist/create-next-app/index.d.ts +3 -0
  88. package/dist/create-next-app/index.d.ts.map +1 -0
  89. package/dist/create-next-app/index.js +8 -0
  90. package/dist/create-next-app/index.js.map +1 -0
  91. package/dist/scaffold/components/createPageComponents.d.ts +6 -0
  92. package/dist/scaffold/components/createPageComponents.d.ts.map +1 -0
  93. package/dist/scaffold/components/createPageComponents.js +62 -0
  94. package/dist/scaffold/components/createPageComponents.js.map +1 -0
  95. package/dist/scaffold/containers/createContainers.d.ts +6 -0
  96. package/dist/scaffold/containers/createContainers.d.ts.map +1 -0
  97. package/dist/scaffold/containers/createContainers.js +48 -0
  98. package/dist/scaffold/containers/createContainers.js.map +1 -0
  99. package/dist/scaffold/index.d.ts +2 -0
  100. package/dist/scaffold/index.d.ts.map +1 -0
  101. package/dist/scaffold/index.js +6 -0
  102. package/dist/scaffold/index.js.map +1 -0
  103. package/dist/scaffold/instance/createBlankInstance.d.ts +8 -0
  104. package/dist/scaffold/instance/createBlankInstance.d.ts.map +1 -0
  105. package/dist/scaffold/instance/createBlankInstance.js +51 -0
  106. package/dist/scaffold/instance/createBlankInstance.js.map +1 -0
  107. package/dist/scaffold/models/createContentModels.d.ts +6 -0
  108. package/dist/scaffold/models/createContentModels.d.ts.map +1 -0
  109. package/dist/scaffold/models/createContentModels.js +70 -0
  110. package/dist/scaffold/models/createContentModels.js.map +1 -0
  111. package/dist/templates/copyDirectory.d.ts +5 -0
  112. package/dist/templates/copyDirectory.d.ts.map +1 -0
  113. package/dist/templates/copyDirectory.js +28 -0
  114. package/dist/templates/copyDirectory.js.map +1 -0
  115. package/dist/templates/copyTemplates.d.ts +8 -0
  116. package/dist/templates/copyTemplates.d.ts.map +1 -0
  117. package/dist/templates/copyTemplates.js +58 -0
  118. package/dist/templates/copyTemplates.js.map +1 -0
  119. package/dist/templates/index.d.ts +2 -0
  120. package/dist/templates/index.d.ts.map +1 -0
  121. package/dist/templates/index.js +6 -0
  122. package/dist/templates/index.js.map +1 -0
  123. package/dist/types/index.d.ts +50 -0
  124. package/dist/types/index.d.ts.map +1 -0
  125. package/dist/types/index.js +3 -0
  126. package/dist/types/index.js.map +1 -0
  127. package/dist/utils/git.d.ts +9 -0
  128. package/dist/utils/git.d.ts.map +1 -0
  129. package/dist/utils/git.js +71 -0
  130. package/dist/utils/git.js.map +1 -0
  131. package/dist/utils/validation.d.ts +45 -0
  132. package/dist/utils/validation.d.ts.map +1 -0
  133. package/dist/utils/validation.js +180 -0
  134. package/dist/utils/validation.js.map +1 -0
  135. package/package.json +45 -0
  136. package/src/agility/api-keys/generateApiKeys.ts +100 -0
  137. package/src/agility/api-keys/getApiKeys.ts +13 -0
  138. package/src/agility/index.ts +3 -0
  139. package/src/agility/instance/createNewInstance.ts +67 -0
  140. package/src/agility/instance/getAvailableInstances.ts +49 -0
  141. package/src/agility/instance/manageInstance.ts +90 -0
  142. package/src/agility/utils/getMgmtAPIUrl.ts +68 -0
  143. package/src/auth/api-key/authenticateWithApiKey.ts +24 -0
  144. package/src/auth/index.ts +3 -0
  145. package/src/auth/oauth/authenticate.ts +165 -0
  146. package/src/auth/oauth/constants.ts +6 -0
  147. package/src/auth/oauth/exchangeCodeForToken.ts +43 -0
  148. package/src/cli/index.ts +281 -0
  149. package/src/cli/promptForMissingOptions.ts +104 -0
  150. package/src/config/env/createEnvFile.ts +30 -0
  151. package/src/config/index.ts +2 -0
  152. package/src/config/mcp/createMcpConfig.ts +30 -0
  153. package/src/config/packages/installAgilityPackages.ts +63 -0
  154. package/src/config/setupProject.ts +31 -0
  155. package/src/create-next-app/createNextApp.ts +75 -0
  156. package/src/create-next-app/index.ts +3 -0
  157. package/src/scaffold/components/createPageComponents.ts +74 -0
  158. package/src/scaffold/containers/createContainers.ts +55 -0
  159. package/src/scaffold/index.ts +2 -0
  160. package/src/scaffold/instance/createBlankInstance.ts +55 -0
  161. package/src/scaffold/models/createContentModels.ts +83 -0
  162. package/src/templates/copyDirectory.ts +24 -0
  163. package/src/templates/copyTemplates.ts +57 -0
  164. package/src/templates/index.ts +2 -0
  165. package/src/types/index.ts +55 -0
  166. package/src/utils/git.ts +74 -0
  167. package/src/utils/validation.ts +184 -0
  168. package/templates/.claude/QUICK-START.md +230 -0
  169. package/templates/.claude/README.md +32 -0
  170. package/templates/.claude/settings.json +8 -0
  171. package/templates/BLANK-INSTANCE-SETUP.md +375 -0
  172. package/templates/DEVELOPMENT.md +160 -0
  173. package/templates/EXAMPLE-PROMPTS.md +643 -0
  174. package/templates/PROMPTS.md +410 -0
  175. package/templates/README.md +281 -0
  176. package/templates/agents.md +429 -0
  177. package/templates/app/[locale]/[...slug]/error.tsx +17 -0
  178. package/templates/app/[locale]/[...slug]/not-found.tsx +9 -0
  179. package/templates/app/[locale]/[...slug]/page.tsx +102 -0
  180. package/templates/app/[locale]/layout.tsx +22 -0
  181. package/templates/app/[locale]/page.tsx +12 -0
  182. package/templates/app/api/dynamic-redirect/route.ts +24 -0
  183. package/templates/app/api/preview/exit/route.ts +34 -0
  184. package/templates/app/api/preview/route.ts +63 -0
  185. package/templates/app/api/revalidate/route.ts +118 -0
  186. package/templates/components/agility-components/RichTextArea.tsx +66 -0
  187. package/templates/components/agility-components/index.ts +30 -0
  188. package/templates/components/agility-pages/MainTemplate.tsx +36 -0
  189. package/templates/components/agility-pages/index.ts +11 -0
  190. package/templates/docs/01-agility-cms-overview.md +139 -0
  191. package/templates/docs/02-page-routing.md +251 -0
  192. package/templates/docs/03-creating-components.md +462 -0
  193. package/templates/docs/04-data-fetching.md +484 -0
  194. package/templates/docs/05-containers-and-lists.md +596 -0
  195. package/templates/docs/06-localization.md +561 -0
  196. package/templates/docs/07-caching-strategies.md +410 -0
  197. package/templates/docs/08-common-components.md +756 -0
  198. package/templates/docs/09-whats-included.md +279 -0
  199. package/templates/docs/10-mcp-server-setup.md +153 -0
  200. package/templates/docs/11-linked-nested-content.md +611 -0
  201. package/templates/docs/README.md +164 -0
  202. package/templates/lib/cms/getAgilityContext.ts +28 -0
  203. package/templates/lib/cms/getAgilityPage.ts +51 -0
  204. package/templates/lib/cms/getAgilitySDK.ts +22 -0
  205. package/templates/lib/cms/getContentItem.ts +20 -0
  206. package/templates/lib/cms/getContentList.ts +19 -0
  207. package/templates/lib/cms/getRedirections.ts +85 -0
  208. package/templates/lib/cms/getSitemapFlat.ts +19 -0
  209. package/templates/lib/cms/getSitemapNested.ts +19 -0
  210. package/templates/lib/env.ts +99 -0
  211. package/templates/lib/i18n/config.ts +28 -0
  212. package/templates/proxy.ts +101 -0
  213. package/tsconfig.json +21 -0
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateProjectName = validateProjectName;
7
+ exports.checkDirectoryExists = checkDirectoryExists;
8
+ exports.isDirectoryEmpty = isDirectoryEmpty;
9
+ exports.validateProjectPath = validateProjectPath;
10
+ exports.isGitInstalled = isGitInstalled;
11
+ exports.displayValidationError = displayValidationError;
12
+ exports.checkDiskSpace = checkDiskSpace;
13
+ exports.validateNodeVersion = validateNodeVersion;
14
+ const fs_1 = __importDefault(require("fs"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const chalk_1 = __importDefault(require("chalk"));
17
+ /**
18
+ * Reserved names that cannot be used as project names
19
+ */
20
+ const RESERVED_NAMES = [
21
+ 'node_modules',
22
+ 'package.json',
23
+ 'package-lock.json',
24
+ 'yarn.lock',
25
+ 'pnpm-lock.yaml',
26
+ 'bun.lockb',
27
+ '.git',
28
+ '.env',
29
+ '.env.local',
30
+ 'dist',
31
+ 'build',
32
+ 'out',
33
+ 'test',
34
+ 'tests',
35
+ '__tests__',
36
+ 'public',
37
+ 'static',
38
+ 'src',
39
+ 'lib',
40
+ 'components',
41
+ 'app',
42
+ 'pages',
43
+ ];
44
+ /**
45
+ * Validates a project name
46
+ */
47
+ function validateProjectName(projectName) {
48
+ // Check if empty
49
+ if (!projectName || projectName.trim().length === 0) {
50
+ return {
51
+ valid: false,
52
+ message: 'Project name cannot be empty',
53
+ };
54
+ }
55
+ // Check for valid characters (lowercase letters, numbers, hyphens, underscores)
56
+ if (!/^[a-z0-9-_]+$/i.test(projectName)) {
57
+ return {
58
+ valid: false,
59
+ message: 'Project name can only contain letters, numbers, hyphens (-), and underscores (_)',
60
+ };
61
+ }
62
+ // Check if it starts with a dot
63
+ if (projectName.startsWith('.')) {
64
+ return {
65
+ valid: false,
66
+ message: 'Project name cannot start with a dot (.)',
67
+ };
68
+ }
69
+ // Check if it starts with a number
70
+ if (/^\d/.test(projectName)) {
71
+ return {
72
+ valid: false,
73
+ message: 'Project name cannot start with a number',
74
+ };
75
+ }
76
+ // Check for reserved names
77
+ if (RESERVED_NAMES.includes(projectName.toLowerCase())) {
78
+ return {
79
+ valid: false,
80
+ message: `"${projectName}" is a reserved name and cannot be used`,
81
+ };
82
+ }
83
+ return { valid: true };
84
+ }
85
+ /**
86
+ * Checks if a directory already exists
87
+ */
88
+ function checkDirectoryExists(projectPath) {
89
+ return fs_1.default.existsSync(projectPath);
90
+ }
91
+ /**
92
+ * Checks if directory is empty
93
+ */
94
+ function isDirectoryEmpty(projectPath) {
95
+ if (!fs_1.default.existsSync(projectPath)) {
96
+ return true;
97
+ }
98
+ const files = fs_1.default.readdirSync(projectPath);
99
+ return files.length === 0 || files.every(file => file === '.git');
100
+ }
101
+ /**
102
+ * Validates that we can create a project at the given path
103
+ */
104
+ function validateProjectPath(projectPath) {
105
+ const exists = checkDirectoryExists(projectPath);
106
+ if (exists) {
107
+ const isEmpty = isDirectoryEmpty(projectPath);
108
+ if (!isEmpty) {
109
+ return {
110
+ valid: false,
111
+ message: `Directory "${path_1.default.basename(projectPath)}" already exists and is not empty.\nPlease choose a different project name or remove the existing directory.`,
112
+ };
113
+ }
114
+ }
115
+ // Check write permissions of parent directory
116
+ const parentDir = path_1.default.dirname(projectPath);
117
+ try {
118
+ fs_1.default.accessSync(parentDir, fs_1.default.constants.W_OK);
119
+ }
120
+ catch (error) {
121
+ return {
122
+ valid: false,
123
+ message: `No write permission in directory "${parentDir}".\nPlease check your permissions or choose a different location.`,
124
+ };
125
+ }
126
+ return { valid: true };
127
+ }
128
+ /**
129
+ * Check if git is installed
130
+ */
131
+ function isGitInstalled() {
132
+ try {
133
+ const { execSync } = require('child_process');
134
+ execSync('git --version', { stdio: 'ignore' });
135
+ return true;
136
+ }
137
+ catch {
138
+ return false;
139
+ }
140
+ }
141
+ /**
142
+ * Displays validation error with helpful formatting
143
+ */
144
+ function displayValidationError(message) {
145
+ console.error('\n' + chalk_1.default.red('✖') + ' ' + chalk_1.default.red.bold('Validation Error'));
146
+ console.error(chalk_1.default.red(message));
147
+ console.error();
148
+ }
149
+ /**
150
+ * Check available disk space (basic check)
151
+ */
152
+ function checkDiskSpace() {
153
+ try {
154
+ const os = require('os');
155
+ // Basic check - Next.js projects typically need at least 500MB
156
+ const REQUIRED_SPACE_MB = 500;
157
+ const tmpdir = os.tmpdir();
158
+ // This is a rough check - not perfect but better than nothing
159
+ return { hasSpace: true };
160
+ }
161
+ catch {
162
+ // If we can't check, assume it's okay
163
+ return { hasSpace: true };
164
+ }
165
+ }
166
+ /**
167
+ * Validate Node.js version
168
+ */
169
+ function validateNodeVersion() {
170
+ const currentVersion = process.version;
171
+ const majorVersion = parseInt(currentVersion.split('.')[0].slice(1), 10);
172
+ if (majorVersion < 18) {
173
+ return {
174
+ valid: false,
175
+ message: `Node.js 18.0.0 or higher is required. You are using ${currentVersion}.\nPlease upgrade Node.js: https://nodejs.org/`,
176
+ };
177
+ }
178
+ return { valid: true };
179
+ }
180
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@agility/create-next-app",
3
+ "version": "1.0.0-beta.2",
4
+ "description": "Create a new Next.js project with Agility CMS integration",
5
+ "main": "dist/cli/index.js",
6
+ "bin": {
7
+ "create-agility-app": "./bin/create-agility-app.js"
8
+ },
9
+ "scripts": {
10
+ "clean": "rm -rf dist",
11
+ "build": "npm run clean && tsc",
12
+ "dev": "tsx src/cli/index.ts",
13
+ "test": "tsx src/cli/index.ts test-app --typescript --tailwind",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "nextjs",
18
+ "agility",
19
+ "cms",
20
+ "cli",
21
+ "scaffold"
22
+ ],
23
+ "author": "",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "@agility/content-fetch": "^2.0.9",
27
+ "boxen": "^8.0.1",
28
+ "chalk": "^5.3.0",
29
+ "commander": "^11.1.0",
30
+ "inquirer": "^9.2.15",
31
+ "open": "^10.1.0",
32
+ "ora": "^8.0.1",
33
+ "update-notifier": "^7.3.1"
34
+ },
35
+ "devDependencies": {
36
+ "@types/inquirer": "^9.0.7",
37
+ "@types/node": "^20.0.0",
38
+ "@types/update-notifier": "^6.0.8",
39
+ "tsx": "^4.7.0",
40
+ "typescript": "^5.0.0"
41
+ },
42
+ "engines": {
43
+ "node": ">=18.0.0"
44
+ }
45
+ }
@@ -0,0 +1,100 @@
1
+ import ora from 'ora';
2
+ import chalk from 'chalk';
3
+ import type { ApiKeys } from '../../types';
4
+ import { getMgmtOAuthUrl } from '../utils/getMgmtAPIUrl';
5
+
6
+ /**
7
+ * Gets API keys for an Agility instance
8
+ * @param accessToken - OAuth access token
9
+ * @param instanceGuid - Instance GUID
10
+ * @returns Object with fetch and preview API keys
11
+ */
12
+ export async function generateApiKeys(accessToken: string, instanceGuid: string): Promise<ApiKeys> {
13
+ const spinner = ora('Fetching API keys...').start();
14
+
15
+ try {
16
+ const oauthBaseUrl = getMgmtOAuthUrl({ guid: instanceGuid });
17
+
18
+ // Get Fetch API Key
19
+ const fetchKeyUrl = `${oauthBaseUrl}GetFetchKey?guid=${instanceGuid}`;
20
+ const fetchKeyResponse = await fetch(fetchKeyUrl, {
21
+ method: 'GET',
22
+ headers: {
23
+ 'Authorization': `Bearer ${accessToken}`,
24
+ 'Content-Type': 'application/json',
25
+ },
26
+ });
27
+
28
+ if (!fetchKeyResponse.ok) {
29
+ const errorText = await fetchKeyResponse.text();
30
+ let errorMessage = `Failed to get Fetch Key: ${fetchKeyResponse.status} ${fetchKeyResponse.statusText}`;
31
+ try {
32
+ const errorJson = JSON.parse(errorText);
33
+ if (errorJson.message) {
34
+ errorMessage += ` - ${errorJson.message}`;
35
+ } else {
36
+ errorMessage += ` - ${errorText}`;
37
+ }
38
+ } catch {
39
+ errorMessage += ` - ${errorText}`;
40
+ }
41
+ throw new Error(errorMessage);
42
+ }
43
+
44
+ const fetchKey = await fetchKeyResponse.text();
45
+ if (!fetchKey) {
46
+ throw new Error('No Fetch Key received from API');
47
+ }
48
+
49
+ // Get Preview API Key
50
+ const previewKeyUrl = `${oauthBaseUrl}GetPreviewKey?guid=${instanceGuid}`;
51
+
52
+ const previewKeyResponse = await fetch(previewKeyUrl, {
53
+ method: 'GET',
54
+ headers: {
55
+ 'Authorization': `Bearer ${accessToken}`,
56
+ 'Content-Type': 'application/json',
57
+ },
58
+ });
59
+
60
+ if (!previewKeyResponse.ok) {
61
+ const errorText = await previewKeyResponse.text();
62
+ let errorMessage = `Failed to get Preview Key: ${previewKeyResponse.status} ${previewKeyResponse.statusText}`;
63
+ try {
64
+ const errorJson = JSON.parse(errorText);
65
+ if (errorJson.message) {
66
+ errorMessage += ` - ${errorJson.message}`;
67
+ } else {
68
+ errorMessage += ` - ${errorText}`;
69
+ }
70
+ } catch {
71
+ errorMessage += ` - ${errorText}`;
72
+ }
73
+ throw new Error(errorMessage);
74
+ }
75
+
76
+ const previewKey = await previewKeyResponse.text();
77
+ if (!previewKey) {
78
+ throw new Error('No Preview Key received from API');
79
+ }
80
+
81
+ spinner.succeed('API keys retrieved');
82
+
83
+ return {
84
+ fetchKey,
85
+ previewKey,
86
+ securityKey: '' // Security key is typically not needed for basic setup
87
+ };
88
+ } catch (error) {
89
+ spinner.fail('Failed to get API keys');
90
+ if (error instanceof Error) {
91
+ console.error(chalk.red(`Error: ${error.message}`));
92
+ // Log additional details if available
93
+ if (error instanceof Error && 'cause' in error) {
94
+ console.error(chalk.gray(`Details: ${JSON.stringify(error.cause)}`));
95
+ }
96
+ }
97
+ throw error;
98
+ }
99
+ }
100
+
@@ -0,0 +1,13 @@
1
+ import type { ApiKeys } from '../../types';
2
+ import { generateApiKeys } from './generateApiKeys';
3
+
4
+ /**
5
+ * Gets API keys for an existing instance
6
+ * @param accessToken - OAuth access token
7
+ * @param instanceGuid - Instance GUID
8
+ * @returns Object with fetch and preview API keys
9
+ */
10
+ export async function getApiKeys(accessToken: string, instanceGuid: string): Promise<ApiKeys> {
11
+ return generateApiKeys(accessToken, instanceGuid);
12
+ }
13
+
@@ -0,0 +1,3 @@
1
+ export { manageInstance } from './instance/manageInstance';
2
+ export { getApiKeys } from './api-keys/getApiKeys';
3
+
@@ -0,0 +1,67 @@
1
+ import inquirer from 'inquirer';
2
+ import ora from 'ora';
3
+ import type { InstanceData } from '../../types';
4
+ import { generateApiKeys } from '../api-keys/generateApiKeys';
5
+
6
+ /**
7
+ * Creates a new blank Agility instance
8
+ * @param accessToken - OAuth access token
9
+ * @returns Instance data with GUID and API keys
10
+ */
11
+ export async function createNewInstance(accessToken: string): Promise<InstanceData> {
12
+ const spinner = ora('Creating new Agility instance...').start();
13
+
14
+ try {
15
+ // Prompt for instance name
16
+ const answers = await inquirer.prompt<{ instanceName: string }>([
17
+ {
18
+ type: 'input',
19
+ name: 'instanceName',
20
+ message: 'Enter a name for your new instance:',
21
+ default: 'My Agility Instance',
22
+ validate: (input: string) => {
23
+ if (!input || input.trim().length === 0) {
24
+ return 'Instance name is required';
25
+ }
26
+ return true;
27
+ }
28
+ }
29
+ ]);
30
+
31
+ spinner.text = 'Creating instance...';
32
+
33
+ // In a real implementation, this would call Agility's API to create an instance
34
+ // For now, we'll use a placeholder that would need to be implemented
35
+ // const instance = await createAgilityInstance(accessToken, answers.instanceName);
36
+
37
+ // For now, we'll prompt for the instance GUID manually
38
+ const guidAnswer = await inquirer.prompt<{ instanceGuid: string }>([
39
+ {
40
+ type: 'input',
41
+ name: 'instanceGuid',
42
+ message: 'Enter the instance GUID (you can find this in Agility CMS):',
43
+ validate: (input: string) => {
44
+ if (!input || input.trim().length === 0) {
45
+ return 'Instance GUID is required';
46
+ }
47
+ return true;
48
+ }
49
+ }
50
+ ]);
51
+
52
+ spinner.succeed('Instance ready');
53
+
54
+ // Generate API keys
55
+ const apiKeys = await generateApiKeys(accessToken, guidAnswer.instanceGuid);
56
+
57
+ return {
58
+ guid: guidAnswer.instanceGuid,
59
+ name: answers.instanceName,
60
+ apiKeys
61
+ };
62
+ } catch (error) {
63
+ spinner.fail('Failed to create instance');
64
+ throw error;
65
+ }
66
+ }
67
+
@@ -0,0 +1,49 @@
1
+ import chalk from 'chalk';
2
+ import type { Instance } from '../../types';
3
+ import { getMgmtAPIUrl } from '../utils/getMgmtAPIUrl';
4
+
5
+ /**
6
+ * Gets available Agility instances for the authenticated user
7
+ * @param accessToken - OAuth access token
8
+ * @returns Array of instances
9
+ */
10
+ export async function getAvailableInstances(accessToken: string): Promise<Instance[]> {
11
+ try {
12
+ // Use the Agility Management API to get current user and their instances
13
+ // Note: We don't have a GUID yet, so we use the default URL
14
+ const apiBaseUrl = getMgmtAPIUrl();
15
+ const response = await fetch(`${apiBaseUrl}users/me`, {
16
+ method: 'GET',
17
+ headers: {
18
+ 'Authorization': `Bearer ${accessToken}`,
19
+ 'Content-Type': 'application/json',
20
+ },
21
+ });
22
+
23
+ if (!response.ok) {
24
+ throw new Error(`Failed to get user info: ${response.status} ${response.statusText}`);
25
+ }
26
+
27
+ const userData = await response.json() as {
28
+ websiteAccess?: Array<{
29
+ websiteName: string;
30
+ displayName: string;
31
+ guid: string;
32
+ orgName?: string;
33
+ }>;
34
+ };
35
+
36
+ // Convert websiteAccess to instances
37
+ const instances: Instance[] = (userData.websiteAccess || []).map(access => ({
38
+ name: access.displayName || access.websiteName,
39
+ guid: access.guid,
40
+ org: access.orgName,
41
+ }));
42
+
43
+ return instances;
44
+ } catch (error) {
45
+ console.warn(chalk.yellow('Could not fetch instances from API'));
46
+ throw error;
47
+ }
48
+ }
49
+
@@ -0,0 +1,90 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import type { InstanceData, CliOptions } from '../../types';
5
+ import { getAvailableInstances } from './getAvailableInstances';
6
+ import { generateApiKeys } from '../api-keys/generateApiKeys';
7
+
8
+ /**
9
+ * Manages Agility CMS instance selection from existing instances
10
+ * @param accessToken - OAuth access token
11
+ * @param options - CLI options
12
+ * @returns Instance data with GUID and API keys
13
+ */
14
+ export async function manageInstance(accessToken: string, options: CliOptions): Promise<InstanceData> {
15
+ const spinner = ora('Fetching Agility instances...').start();
16
+
17
+ try {
18
+ // Fetch available instances using the Management API
19
+ const instances = await getAvailableInstances(accessToken);
20
+
21
+ spinner.succeed(`Found ${instances.length} instance(s)`);
22
+
23
+ if (instances.length === 0) {
24
+ throw new Error('No Agility CMS instances found. Please create an instance in Agility CMS first, or skip the connection for now.');
25
+ }
26
+
27
+ // Group instances by organization
28
+ const instancesByOrg = new Map<string, typeof instances>();
29
+ for (const inst of instances) {
30
+ const orgName = inst.org || 'No Organization';
31
+ if (!instancesByOrg.has(orgName)) {
32
+ instancesByOrg.set(orgName, []);
33
+ }
34
+ instancesByOrg.get(orgName)!.push(inst);
35
+ }
36
+
37
+ // Sort organizations alphabetically
38
+ const sortedOrgs = Array.from(instancesByOrg.keys()).sort();
39
+
40
+ // Build choices with organization grouping
41
+ const instanceChoices: Array<{ name: string; value: string } | inquirer.Separator> = [];
42
+
43
+ for (const orgName of sortedOrgs) {
44
+ // Add separator for organization (except for first one)
45
+ if (instanceChoices.length > 0) {
46
+ instanceChoices.push(new inquirer.Separator());
47
+ }
48
+
49
+ // Add organization header
50
+ instanceChoices.push(new inquirer.Separator(chalk.cyan(` ${orgName}`)));
51
+
52
+ // Add instances for this organization
53
+ const orgInstances = instancesByOrg.get(orgName)!;
54
+ for (const inst of orgInstances) {
55
+ instanceChoices.push({
56
+ name: ` - ${inst.name} (${inst.guid})`,
57
+ value: inst.guid
58
+ });
59
+ }
60
+ }
61
+
62
+ const selected = await inquirer.prompt<{ instanceGuid: string }>([
63
+ {
64
+ type: 'list',
65
+ name: 'instanceGuid',
66
+ message: 'Select an instance:',
67
+ choices: instanceChoices
68
+ }
69
+ ]);
70
+
71
+ // Find the selected instance to get its name
72
+ const selectedInstance = instances.find(inst => inst.guid === selected.instanceGuid);
73
+ if (!selectedInstance) {
74
+ throw new Error('Selected instance not found');
75
+ }
76
+
77
+ // Generate API keys for selected instance
78
+ const apiKeys = await generateApiKeys(accessToken, selected.instanceGuid);
79
+
80
+ return {
81
+ guid: selected.instanceGuid,
82
+ name: selectedInstance.name,
83
+ apiKeys
84
+ };
85
+ } catch (error) {
86
+ spinner.fail('Failed to manage instance');
87
+ throw error;
88
+ }
89
+ }
90
+
@@ -0,0 +1,68 @@
1
+ interface Props {
2
+ /**
3
+ * The instance guid (optional - if not provided, uses default URL)
4
+ */
5
+ guid?: string;
6
+ }
7
+
8
+ /**
9
+ * Gets the management API base URL based on the instance GUID
10
+ * @param guid - The instance GUID (optional - if not provided, uses default URL)
11
+ * @returns The base URL for the management API
12
+ */
13
+ export const getMgmtAPIUrl = ({ guid }: Props = {}): string => {
14
+ const baseUrlSuffixes: { [key: string]: string } = {
15
+ u: '',
16
+ c: '-ca',
17
+ e: '-eu',
18
+ a: '-aus',
19
+ d: '-dev'
20
+ };
21
+
22
+ // If no GUID provided, use default URL
23
+ if (!guid || guid.length === 0) {
24
+ return `https://mgmt.aglty.io/api/v1/`;
25
+ }
26
+
27
+ const env = guid.substring(guid.length - 1);
28
+
29
+ // New format of guid
30
+ if (baseUrlSuffixes.hasOwnProperty(env)) {
31
+ return `https://mgmt${baseUrlSuffixes[env]}.aglty.io/api/v1/`;
32
+ } else {
33
+ // use default url
34
+ return `https://mgmt.aglty.io/api/v1/`;
35
+ }
36
+ };
37
+
38
+ /**
39
+ * Gets the management OAuth base URL based on the instance GUID
40
+ * @param guid - The instance GUID (optional - if not provided, uses default URL)
41
+ * @returns The base URL for the management OAuth endpoints
42
+ */
43
+ export const getMgmtOAuthUrl = ({ guid }: Props = {}): string => {
44
+ const baseUrlSuffixes: { [key: string]: string } = {
45
+ u: '',
46
+ c: '-ca',
47
+ e: '-eu',
48
+ a: '-aus',
49
+ d: '-dev',
50
+ us2: 'usa2'
51
+ };
52
+
53
+ // If no GUID provided, use default URL
54
+ if (!guid || guid.length === 0) {
55
+ return `https://mgmt.aglty.io/oauth/`;
56
+ }
57
+
58
+ const env = guid.substring(guid.length - 1);
59
+
60
+ // New format of guid
61
+ if (baseUrlSuffixes.hasOwnProperty(env)) {
62
+ return `https://mgmt${baseUrlSuffixes[env]}.aglty.io/oauth/`;
63
+ } else {
64
+ // use default url
65
+ return `https://mgmt.aglty.io/oauth/`;
66
+ }
67
+ };
68
+
@@ -0,0 +1,24 @@
1
+ import inquirer from 'inquirer';
2
+
3
+ /**
4
+ * Alternative: Manual API key input if OAuth is not available
5
+ * @returns API key or access token
6
+ */
7
+ export async function authenticateWithApiKey(): Promise<string> {
8
+ const answers = await inquirer.prompt<{ apiKey: string }>([
9
+ {
10
+ type: 'input',
11
+ name: 'apiKey',
12
+ message: 'Enter your Agility CMS API key:',
13
+ validate: (input: string) => {
14
+ if (!input || input.trim().length === 0) {
15
+ return 'API key is required';
16
+ }
17
+ return true;
18
+ }
19
+ }
20
+ ]);
21
+
22
+ return answers.apiKey;
23
+ }
24
+
@@ -0,0 +1,3 @@
1
+ export { authenticate } from './oauth/authenticate';
2
+ export { authenticateWithApiKey } from './api-key/authenticateWithApiKey';
3
+