primitive-admin 1.1.0-alpha.4 → 1.1.0-alpha.41

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 (219) hide show
  1. package/README.md +325 -31
  2. package/assets/skill/skills/primitive-platform/SKILL.md +281 -0
  3. package/dist/bin/primitive.d.ts +2 -0
  4. package/dist/bin/primitive.js +268 -16
  5. package/dist/bin/primitive.js.map +1 -1
  6. package/dist/src/commands/admins.d.ts +2 -0
  7. package/dist/src/commands/admins.js +123 -18
  8. package/dist/src/commands/admins.js.map +1 -1
  9. package/dist/src/commands/analytics.d.ts +2 -0
  10. package/dist/src/commands/analytics.js +464 -55
  11. package/dist/src/commands/analytics.js.map +1 -1
  12. package/dist/src/commands/apps.d.ts +2 -0
  13. package/dist/src/commands/apps.js +55 -13
  14. package/dist/src/commands/apps.js.map +1 -1
  15. package/dist/src/commands/auth.d.ts +2 -0
  16. package/dist/src/commands/auth.js +177 -7
  17. package/dist/src/commands/auth.js.map +1 -1
  18. package/dist/src/commands/blob-buckets.d.ts +2 -0
  19. package/dist/src/commands/blob-buckets.js +422 -0
  20. package/dist/src/commands/blob-buckets.js.map +1 -0
  21. package/dist/src/commands/catalog.d.ts +2 -0
  22. package/dist/src/commands/catalog.js +34 -35
  23. package/dist/src/commands/catalog.js.map +1 -1
  24. package/dist/src/commands/collection-type-configs.d.ts +2 -0
  25. package/dist/src/commands/collection-type-configs.js +178 -0
  26. package/dist/src/commands/collection-type-configs.js.map +1 -0
  27. package/dist/src/commands/collections.d.ts +2 -0
  28. package/dist/src/commands/collections.js +515 -0
  29. package/dist/src/commands/collections.js.map +1 -0
  30. package/dist/src/commands/comparisons.d.ts +2 -0
  31. package/dist/src/commands/comparisons.js +6 -6
  32. package/dist/src/commands/comparisons.js.map +1 -1
  33. package/dist/src/commands/cron-triggers.d.ts +2 -0
  34. package/dist/src/commands/cron-triggers.js +357 -0
  35. package/dist/src/commands/cron-triggers.js.map +1 -0
  36. package/dist/src/commands/database-types.d.ts +2 -0
  37. package/dist/src/commands/database-types.js +471 -0
  38. package/dist/src/commands/database-types.js.map +1 -0
  39. package/dist/src/commands/databases.d.ts +2 -0
  40. package/dist/src/commands/databases.js +1462 -83
  41. package/dist/src/commands/databases.js.map +1 -1
  42. package/dist/src/commands/documents.d.ts +2 -0
  43. package/dist/src/commands/documents.js +518 -11
  44. package/dist/src/commands/documents.js.map +1 -1
  45. package/dist/src/commands/email-templates.d.ts +2 -0
  46. package/dist/src/commands/email-templates.js +281 -0
  47. package/dist/src/commands/email-templates.js.map +1 -0
  48. package/dist/src/commands/env.d.ts +12 -0
  49. package/dist/src/commands/env.js +260 -0
  50. package/dist/src/commands/env.js.map +1 -0
  51. package/dist/src/commands/group-type-configs.d.ts +2 -0
  52. package/dist/src/commands/group-type-configs.js +181 -0
  53. package/dist/src/commands/group-type-configs.js.map +1 -0
  54. package/dist/src/commands/groups.d.ts +2 -0
  55. package/dist/src/commands/groups.js +48 -45
  56. package/dist/src/commands/groups.js.map +1 -1
  57. package/dist/src/commands/guides.d.ts +159 -0
  58. package/dist/src/commands/guides.js +449 -35
  59. package/dist/src/commands/guides.js.map +1 -1
  60. package/dist/src/commands/init.d.ts +17 -0
  61. package/dist/src/commands/init.js +811 -201
  62. package/dist/src/commands/init.js.map +1 -1
  63. package/dist/src/commands/integrations.d.ts +2 -0
  64. package/dist/src/commands/integrations.js +533 -46
  65. package/dist/src/commands/integrations.js.map +1 -1
  66. package/dist/src/commands/llm.d.ts +2 -0
  67. package/dist/src/commands/llm.js +4 -2
  68. package/dist/src/commands/llm.js.map +1 -1
  69. package/dist/src/commands/metadata.d.ts +2 -0
  70. package/dist/src/commands/metadata.js +194 -0
  71. package/dist/src/commands/metadata.js.map +1 -0
  72. package/dist/src/commands/prompts.d.ts +2 -0
  73. package/dist/src/commands/prompts.js +50 -52
  74. package/dist/src/commands/prompts.js.map +1 -1
  75. package/dist/src/commands/rule-sets.d.ts +3 -0
  76. package/dist/src/commands/rule-sets.js +388 -0
  77. package/dist/src/commands/rule-sets.js.map +1 -0
  78. package/dist/src/commands/scripts.d.ts +2 -0
  79. package/dist/src/commands/scripts.js +667 -0
  80. package/dist/src/commands/scripts.js.map +1 -0
  81. package/dist/src/commands/secrets.d.ts +2 -0
  82. package/dist/src/commands/secrets.js +108 -0
  83. package/dist/src/commands/secrets.js.map +1 -0
  84. package/dist/src/commands/skill.d.ts +2 -0
  85. package/dist/src/commands/skill.js +29 -0
  86. package/dist/src/commands/skill.js.map +1 -0
  87. package/dist/src/commands/sync.d.ts +175 -0
  88. package/dist/src/commands/sync.js +4412 -233
  89. package/dist/src/commands/sync.js.map +1 -1
  90. package/dist/src/commands/tokens.d.ts +2 -0
  91. package/dist/src/commands/tokens.js +122 -19
  92. package/dist/src/commands/tokens.js.map +1 -1
  93. package/dist/src/commands/users.d.ts +2 -0
  94. package/dist/src/commands/users.js +440 -22
  95. package/dist/src/commands/users.js.map +1 -1
  96. package/dist/src/commands/waitlist.d.ts +2 -0
  97. package/dist/src/commands/waitlist.js +10 -10
  98. package/dist/src/commands/waitlist.js.map +1 -1
  99. package/dist/src/commands/webhooks.d.ts +2 -0
  100. package/dist/src/commands/webhooks.js +391 -0
  101. package/dist/src/commands/webhooks.js.map +1 -0
  102. package/dist/src/commands/workflows.d.ts +49 -0
  103. package/dist/src/commands/workflows.js +1009 -123
  104. package/dist/src/commands/workflows.js.map +1 -1
  105. package/dist/src/lib/api-client.d.ts +1283 -0
  106. package/dist/src/lib/api-client.js +1129 -83
  107. package/dist/src/lib/api-client.js.map +1 -1
  108. package/dist/src/lib/auth-flow.d.ts +8 -0
  109. package/dist/src/lib/block-layout.d.ts +160 -0
  110. package/dist/src/lib/block-layout.js +451 -0
  111. package/dist/src/lib/block-layout.js.map +1 -0
  112. package/dist/src/lib/cli-manifest.d.ts +60 -0
  113. package/dist/src/lib/cli-manifest.js +70 -0
  114. package/dist/src/lib/cli-manifest.js.map +1 -0
  115. package/dist/src/lib/config.d.ts +53 -0
  116. package/dist/src/lib/config.js +92 -53
  117. package/dist/src/lib/config.js.map +1 -1
  118. package/dist/src/lib/confirm-prompt.d.ts +83 -0
  119. package/dist/src/lib/confirm-prompt.js +110 -0
  120. package/dist/src/lib/confirm-prompt.js.map +1 -0
  121. package/dist/src/lib/constants.d.ts +2 -0
  122. package/dist/src/lib/constants.js +3 -0
  123. package/dist/src/lib/constants.js.map +1 -0
  124. package/dist/src/lib/crash-handlers.d.ts +20 -0
  125. package/dist/src/lib/crash-handlers.js +49 -0
  126. package/dist/src/lib/crash-handlers.js.map +1 -0
  127. package/dist/src/lib/credentials-store.d.ts +79 -0
  128. package/dist/src/lib/credentials-store.js +307 -0
  129. package/dist/src/lib/credentials-store.js.map +1 -0
  130. package/dist/src/lib/csv.d.ts +48 -0
  131. package/dist/src/lib/csv.js +177 -0
  132. package/dist/src/lib/csv.js.map +1 -0
  133. package/dist/src/lib/db-codegen/dbFingerprint.d.ts +10 -0
  134. package/dist/src/lib/db-codegen/dbFingerprint.js +17 -0
  135. package/dist/src/lib/db-codegen/dbFingerprint.js.map +1 -0
  136. package/dist/src/lib/db-codegen/dbGenerator.d.ts +111 -0
  137. package/dist/src/lib/db-codegen/dbGenerator.js +371 -0
  138. package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -0
  139. package/dist/src/lib/db-codegen/dbNaming.d.ts +45 -0
  140. package/dist/src/lib/db-codegen/dbNaming.js +104 -0
  141. package/dist/src/lib/db-codegen/dbNaming.js.map +1 -0
  142. package/dist/src/lib/db-codegen/dbTemplates.d.ts +97 -0
  143. package/dist/src/lib/db-codegen/dbTemplates.js +159 -0
  144. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -0
  145. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +78 -0
  146. package/dist/src/lib/db-codegen/dbTsTypes.js +112 -0
  147. package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -0
  148. package/dist/src/lib/env-resolver.d.ts +62 -0
  149. package/dist/src/lib/env-resolver.js +121 -0
  150. package/dist/src/lib/env-resolver.js.map +1 -0
  151. package/dist/src/lib/fetch.d.ts +5 -0
  152. package/dist/src/lib/generated-allowlist.d.ts +28 -0
  153. package/dist/src/lib/generated-allowlist.js +220 -0
  154. package/dist/src/lib/generated-allowlist.js.map +1 -0
  155. package/dist/src/lib/init-config.d.ts +59 -0
  156. package/dist/src/lib/init-config.js +113 -0
  157. package/dist/src/lib/init-config.js.map +1 -0
  158. package/dist/src/lib/migration-nag.d.ts +49 -0
  159. package/dist/src/lib/migration-nag.js +163 -0
  160. package/dist/src/lib/migration-nag.js.map +1 -0
  161. package/dist/src/lib/output.d.ts +86 -0
  162. package/dist/src/lib/output.js +150 -8
  163. package/dist/src/lib/output.js.map +1 -1
  164. package/dist/src/lib/paginate.d.ts +33 -0
  165. package/dist/src/lib/paginate.js +42 -0
  166. package/dist/src/lib/paginate.js.map +1 -0
  167. package/dist/src/lib/project-config.d.ts +97 -0
  168. package/dist/src/lib/project-config.js +209 -0
  169. package/dist/src/lib/project-config.js.map +1 -0
  170. package/dist/src/lib/query-operators.d.ts +43 -0
  171. package/dist/src/lib/query-operators.js +80 -0
  172. package/dist/src/lib/query-operators.js.map +1 -0
  173. package/dist/src/lib/refresh-admin-credentials.d.ts +65 -0
  174. package/dist/src/lib/refresh-admin-credentials.js +103 -0
  175. package/dist/src/lib/refresh-admin-credentials.js.map +1 -0
  176. package/dist/src/lib/resolve-platform.d.ts +45 -0
  177. package/dist/src/lib/resolve-platform.js +43 -0
  178. package/dist/src/lib/resolve-platform.js.map +1 -0
  179. package/dist/src/lib/skill-installer.d.ts +23 -0
  180. package/dist/src/lib/skill-installer.js +135 -0
  181. package/dist/src/lib/skill-installer.js.map +1 -0
  182. package/dist/src/lib/snapshots.d.ts +99 -0
  183. package/dist/src/lib/snapshots.js +357 -0
  184. package/dist/src/lib/snapshots.js.map +1 -0
  185. package/dist/src/lib/sync-paths.d.ts +72 -0
  186. package/dist/src/lib/sync-paths.js +130 -0
  187. package/dist/src/lib/sync-paths.js.map +1 -0
  188. package/dist/src/lib/template.d.ts +97 -0
  189. package/dist/src/lib/template.js +336 -62
  190. package/dist/src/lib/template.js.map +1 -1
  191. package/dist/src/lib/token-inject.d.ts +56 -0
  192. package/dist/src/lib/token-inject.js +204 -0
  193. package/dist/src/lib/token-inject.js.map +1 -0
  194. package/dist/src/lib/toml-database-config.d.ts +152 -0
  195. package/dist/src/lib/toml-database-config.js +600 -0
  196. package/dist/src/lib/toml-database-config.js.map +1 -0
  197. package/dist/src/lib/toml-metadata-config.d.ts +139 -0
  198. package/dist/src/lib/toml-metadata-config.js +427 -0
  199. package/dist/src/lib/toml-metadata-config.js.map +1 -0
  200. package/dist/src/lib/toml-params-validator.d.ts +129 -0
  201. package/dist/src/lib/toml-params-validator.js +298 -0
  202. package/dist/src/lib/toml-params-validator.js.map +1 -0
  203. package/dist/src/lib/version-check.d.ts +10 -0
  204. package/dist/src/lib/version-check.js +172 -0
  205. package/dist/src/lib/version-check.js.map +1 -0
  206. package/dist/src/lib/workflow-apply.d.ts +66 -0
  207. package/dist/src/lib/workflow-apply.js +117 -0
  208. package/dist/src/lib/workflow-apply.js.map +1 -0
  209. package/dist/src/lib/workflow-fragments.d.ts +41 -0
  210. package/dist/src/lib/workflow-fragments.js +121 -0
  211. package/dist/src/lib/workflow-fragments.js.map +1 -0
  212. package/dist/src/lib/workflow-toml-validator.d.ts +95 -0
  213. package/dist/src/lib/workflow-toml-validator.js +323 -0
  214. package/dist/src/lib/workflow-toml-validator.js.map +1 -0
  215. package/dist/src/types/index.d.ts +520 -0
  216. package/dist/src/validators.d.ts +64 -0
  217. package/dist/src/validators.js +63 -0
  218. package/dist/src/validators.js.map +1 -0
  219. package/package.json +18 -4
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Project-scoped configuration (.primitive/config.json).
3
+ *
4
+ * This module handles loading, validating, and saving the project config file
5
+ * that lives inside the .primitive/ directory at the project root. The config
6
+ * defines named environments (dev, prod, staging, etc.) with their API URLs
7
+ * and app IDs.
8
+ *
9
+ * Design:
10
+ * - Config file: .primitive/config.json (or any ancestor directory)
11
+ * - The .primitive/ directory groups the committable config and the
12
+ * gitignored local state (credentials, sync data) in one place.
13
+ * - Format: JSON
14
+ * - Schema is versioned (currently 1). Future versions use semver-ish
15
+ * caret range compatibility: we accept any config where major version
16
+ * matches CURRENT_CONFIG_VERSION.
17
+ * - Invalid configs throw — we do NOT silently ignore malformed files.
18
+ */
19
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
20
+ import { dirname, join, resolve } from "path";
21
+ /**
22
+ * Current schema version. Increment the major version when making
23
+ * backward-incompatible changes. Older configs will be rejected.
24
+ */
25
+ export const CURRENT_CONFIG_VERSION = 1;
26
+ /**
27
+ * The config file lives inside the .primitive/ directory, not at the
28
+ * project root. This keeps the project root clean and groups all
29
+ * Primitive state (config, credentials, sync data) under one directory.
30
+ */
31
+ export const PROJECT_CONFIG_DIR = ".primitive";
32
+ export const PROJECT_CONFIG_FILENAME = "config.json";
33
+ /** Display name used in user-facing messages. */
34
+ export const PROJECT_CONFIG_DISPLAY_NAME = ".primitive/config.json";
35
+ export class ProjectConfigError extends Error {
36
+ path;
37
+ constructor(message, path) {
38
+ super(message);
39
+ this.path = path;
40
+ this.name = "ProjectConfigError";
41
+ }
42
+ }
43
+ /**
44
+ * Walks upward from `startDir` looking for a `.primitive/config.json` file.
45
+ * Returns the absolute path to the config file, or null if not found.
46
+ *
47
+ * Allows running `primitive` commands from subdirectories of a project,
48
+ * much like `git` walks up to find `.git`.
49
+ */
50
+ export function findProjectConfigPath(startDir = process.cwd()) {
51
+ // Allow override via env var for testing / unusual setups.
52
+ if (process.env.PRIMITIVE_PROJECT_CONFIG) {
53
+ const forced = resolve(process.env.PRIMITIVE_PROJECT_CONFIG);
54
+ return existsSync(forced) ? forced : null;
55
+ }
56
+ let current = resolve(startDir);
57
+ // Walk up until we hit filesystem root.
58
+ while (true) {
59
+ const candidate = join(current, PROJECT_CONFIG_DIR, PROJECT_CONFIG_FILENAME);
60
+ if (existsSync(candidate))
61
+ return candidate;
62
+ const parent = dirname(current);
63
+ if (parent === current)
64
+ return null;
65
+ current = parent;
66
+ }
67
+ }
68
+ /**
69
+ * Returns the project root directory (the parent of .primitive/ that contains
70
+ * .primitive/config.json), or null if no project config is found.
71
+ *
72
+ * When the PRIMITIVE_PROJECT_CONFIG env var override is active, the config
73
+ * file may not live inside a `.primitive/` directory (e.g. in tests), so
74
+ * we simply go up one level from the file.
75
+ */
76
+ export function findProjectRoot(startDir = process.cwd()) {
77
+ const path = findProjectConfigPath(startDir);
78
+ if (!path)
79
+ return null;
80
+ // When using the env var override, the file could be anywhere — just go
81
+ // up one level. In the normal case the path ends with .primitive/config.json
82
+ // so dirname(dirname(path)) gives the project root.
83
+ if (process.env.PRIMITIVE_PROJECT_CONFIG) {
84
+ return dirname(path);
85
+ }
86
+ return dirname(dirname(path));
87
+ }
88
+ /**
89
+ * Validates a raw parsed object against the ProjectConfig schema.
90
+ * Throws ProjectConfigError with a helpful message on any problem.
91
+ */
92
+ export function validateProjectConfig(raw, sourcePath) {
93
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
94
+ throw new ProjectConfigError(`${PROJECT_CONFIG_DISPLAY_NAME} must contain a JSON object at the top level.`, sourcePath);
95
+ }
96
+ const obj = raw;
97
+ // Version is required.
98
+ if (typeof obj.version !== "number" || !Number.isInteger(obj.version)) {
99
+ throw new ProjectConfigError(`${PROJECT_CONFIG_DISPLAY_NAME} is missing required integer field "version".`, sourcePath);
100
+ }
101
+ const version = obj.version;
102
+ // Caret-range compatibility: accept any config whose major version matches.
103
+ // For version 1.x this is simply version === 1; when we ship v2 we'll
104
+ // reject v1 configs and tell the user to migrate.
105
+ if (version !== CURRENT_CONFIG_VERSION) {
106
+ if (version > CURRENT_CONFIG_VERSION) {
107
+ throw new ProjectConfigError(`${PROJECT_CONFIG_DISPLAY_NAME} has version ${version}, but this CLI only understands version ${CURRENT_CONFIG_VERSION}. Please upgrade the CLI: npm install -g primitive-admin`, sourcePath);
108
+ }
109
+ throw new ProjectConfigError(`${PROJECT_CONFIG_DISPLAY_NAME} has unsupported version ${version}. This CLI expects version ${CURRENT_CONFIG_VERSION}.`, sourcePath);
110
+ }
111
+ // environments is required.
112
+ if (!obj.environments || typeof obj.environments !== "object" || Array.isArray(obj.environments)) {
113
+ throw new ProjectConfigError(`${PROJECT_CONFIG_DISPLAY_NAME} is missing required "environments" object.`, sourcePath);
114
+ }
115
+ const environments = {};
116
+ for (const [name, envRaw] of Object.entries(obj.environments)) {
117
+ if (!name.match(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/)) {
118
+ throw new ProjectConfigError(`Invalid environment name "${name}" in ${PROJECT_CONFIG_DISPLAY_NAME}. Names must be alphanumeric with optional dashes/underscores.`, sourcePath);
119
+ }
120
+ if (!envRaw || typeof envRaw !== "object" || Array.isArray(envRaw)) {
121
+ throw new ProjectConfigError(`Environment "${name}" must be an object in ${PROJECT_CONFIG_DISPLAY_NAME}.`, sourcePath);
122
+ }
123
+ const envObj = envRaw;
124
+ if (typeof envObj.apiUrl !== "string" || !envObj.apiUrl) {
125
+ throw new ProjectConfigError(`Environment "${name}" must have a non-empty "apiUrl" string in ${PROJECT_CONFIG_DISPLAY_NAME}.`, sourcePath);
126
+ }
127
+ try {
128
+ new URL(envObj.apiUrl);
129
+ }
130
+ catch {
131
+ throw new ProjectConfigError(`Environment "${name}" has invalid apiUrl: ${envObj.apiUrl}`, sourcePath);
132
+ }
133
+ if (envObj.appId !== undefined && typeof envObj.appId !== "string") {
134
+ throw new ProjectConfigError(`Environment "${name}": "appId" must be a string if set.`, sourcePath);
135
+ }
136
+ if (envObj.appName !== undefined && typeof envObj.appName !== "string") {
137
+ throw new ProjectConfigError(`Environment "${name}": "appName" must be a string if set.`, sourcePath);
138
+ }
139
+ if (envObj.description !== undefined && typeof envObj.description !== "string") {
140
+ throw new ProjectConfigError(`Environment "${name}": "description" must be a string if set.`, sourcePath);
141
+ }
142
+ environments[name] = {
143
+ apiUrl: envObj.apiUrl.replace(/\/$/, ""),
144
+ appId: envObj.appId,
145
+ appName: envObj.appName,
146
+ description: envObj.description,
147
+ };
148
+ }
149
+ // defaultEnvironment is optional but must refer to a real environment.
150
+ let defaultEnvironment;
151
+ if (obj.defaultEnvironment !== undefined) {
152
+ if (typeof obj.defaultEnvironment !== "string") {
153
+ throw new ProjectConfigError(`"defaultEnvironment" must be a string if set.`, sourcePath);
154
+ }
155
+ if (obj.defaultEnvironment && !environments[obj.defaultEnvironment]) {
156
+ throw new ProjectConfigError(`"defaultEnvironment" is set to "${obj.defaultEnvironment}" but no such environment is defined.`, sourcePath);
157
+ }
158
+ defaultEnvironment = obj.defaultEnvironment || undefined;
159
+ }
160
+ return { version, defaultEnvironment, environments };
161
+ }
162
+ /**
163
+ * Loads and validates the project config from a specific file path.
164
+ * Throws ProjectConfigError on I/O errors, JSON parse errors, or schema
165
+ * validation failures — we deliberately do NOT swallow these, because a
166
+ * broken config should be noisy so the user can fix it.
167
+ */
168
+ export function loadProjectConfigFromPath(path) {
169
+ let raw;
170
+ try {
171
+ raw = readFileSync(path, "utf-8");
172
+ }
173
+ catch (err) {
174
+ throw new ProjectConfigError(`Failed to read ${PROJECT_CONFIG_DISPLAY_NAME}: ${err.message}`, path);
175
+ }
176
+ let parsed;
177
+ try {
178
+ parsed = JSON.parse(raw);
179
+ }
180
+ catch (err) {
181
+ throw new ProjectConfigError(`Failed to parse ${PROJECT_CONFIG_DISPLAY_NAME}: ${err.message}`, path);
182
+ }
183
+ return validateProjectConfig(parsed, path);
184
+ }
185
+ /**
186
+ * Loads the project config by searching upward from the current directory.
187
+ * Returns null (not an error) if no .primitive/config.json is found anywhere.
188
+ * Still throws ProjectConfigError if a config IS found but is invalid.
189
+ */
190
+ export function loadProjectConfig(startDir = process.cwd()) {
191
+ const path = findProjectConfigPath(startDir);
192
+ if (!path)
193
+ return null;
194
+ return loadProjectConfigFromPath(path);
195
+ }
196
+ /**
197
+ * Writes a project config to disk as pretty-printed JSON. Callers are
198
+ * responsible for ensuring the parent directory exists — this function
199
+ * does not try to be clever about location.
200
+ */
201
+ export function saveProjectConfig(path, config) {
202
+ const dir = dirname(path);
203
+ if (!existsSync(dir)) {
204
+ mkdirSync(dir, { recursive: true });
205
+ }
206
+ const serialized = JSON.stringify(config, null, 2) + "\n";
207
+ writeFileSync(path, serialized);
208
+ }
209
+ //# sourceMappingURL=project-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-config.js","sourceRoot":"","sources":["../../../src/lib/project-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Shared constant for the query operators an operation `filter` may use,
3
+ * plus a helper to find operator keys that aren't in the supported set.
4
+ *
5
+ * Background (issue #1255): a database-operation `filter` uses Mongo-style
6
+ * operators (`$gte`, `$in`, ...). A typo like `$gt` → `$gte` or an invented
7
+ * `$foo` silently passes `sync push` (the server only validates `filter` is
8
+ * an object) and then throws `InvalidOperatorError` at *query execution*.
9
+ * The CLI can warn about unknown operators at push time so the author fixes
10
+ * it before it reaches runtime.
11
+ *
12
+ * The set mirrors the runtime translator
13
+ * (`packages/js-bao/src/query/JsonQueryTranslator.ts`): field operators
14
+ * (`translateFieldCondition` / substring ops) plus the two logical
15
+ * combinators special-cased in `translateFilter` (`$and` / `$or`).
16
+ *
17
+ * `$not` is intentionally NOT included — it is not a runtime operator; the
18
+ * translator only special-cases `$and` / `$or`, so `$not` would throw at
19
+ * execution just like any other unknown operator (codex review, #1255).
20
+ *
21
+ * This is deliberately a single source of truth so the validator and any
22
+ * future emitter/detector agree on what "known" means.
23
+ */
24
+ /** Field-level comparison / membership / string operators. */
25
+ export declare const FIELD_QUERY_OPERATORS: readonly string[];
26
+ /** Logical combinators (arrays of sub-filters). */
27
+ export declare const LOGICAL_QUERY_OPERATORS: readonly string[];
28
+ /**
29
+ * Every operator the runtime query translator accepts. Any `$`-prefixed
30
+ * *key* inside a filter that is not in this set is unknown.
31
+ */
32
+ export declare const SUPPORTED_QUERY_OPERATORS: ReadonlySet<string>;
33
+ /**
34
+ * Recursively collect the distinct `$`-prefixed *keys* used in a filter (or
35
+ * any nested value) that are not in {@link SUPPORTED_QUERY_OPERATORS}.
36
+ *
37
+ * Only object *keys* are considered operators — matching the runtime
38
+ * convention (`walk-operation.ts`: `$`-prefixed keys are operators). String
39
+ * *values* like `$params.activeSince` are substitution references, never
40
+ * operators, so they are never flagged. `$and` / `$or` values are arrays of
41
+ * sub-filter objects and are walked recursively.
42
+ */
43
+ export declare function collectUnknownOperators(value: any): string[];
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Shared constant for the query operators an operation `filter` may use,
3
+ * plus a helper to find operator keys that aren't in the supported set.
4
+ *
5
+ * Background (issue #1255): a database-operation `filter` uses Mongo-style
6
+ * operators (`$gte`, `$in`, ...). A typo like `$gt` → `$gte` or an invented
7
+ * `$foo` silently passes `sync push` (the server only validates `filter` is
8
+ * an object) and then throws `InvalidOperatorError` at *query execution*.
9
+ * The CLI can warn about unknown operators at push time so the author fixes
10
+ * it before it reaches runtime.
11
+ *
12
+ * The set mirrors the runtime translator
13
+ * (`packages/js-bao/src/query/JsonQueryTranslator.ts`): field operators
14
+ * (`translateFieldCondition` / substring ops) plus the two logical
15
+ * combinators special-cased in `translateFilter` (`$and` / `$or`).
16
+ *
17
+ * `$not` is intentionally NOT included — it is not a runtime operator; the
18
+ * translator only special-cases `$and` / `$or`, so `$not` would throw at
19
+ * execution just like any other unknown operator (codex review, #1255).
20
+ *
21
+ * This is deliberately a single source of truth so the validator and any
22
+ * future emitter/detector agree on what "known" means.
23
+ */
24
+ /** Field-level comparison / membership / string operators. */
25
+ export const FIELD_QUERY_OPERATORS = [
26
+ "$eq",
27
+ "$ne",
28
+ "$gt",
29
+ "$gte",
30
+ "$lt",
31
+ "$lte",
32
+ "$in",
33
+ "$nin",
34
+ "$exists",
35
+ "$contains",
36
+ "$startsWith",
37
+ "$endsWith",
38
+ "$containsText",
39
+ ];
40
+ /** Logical combinators (arrays of sub-filters). */
41
+ export const LOGICAL_QUERY_OPERATORS = ["$and", "$or"];
42
+ /**
43
+ * Every operator the runtime query translator accepts. Any `$`-prefixed
44
+ * *key* inside a filter that is not in this set is unknown.
45
+ */
46
+ export const SUPPORTED_QUERY_OPERATORS = new Set([
47
+ ...FIELD_QUERY_OPERATORS,
48
+ ...LOGICAL_QUERY_OPERATORS,
49
+ ]);
50
+ /**
51
+ * Recursively collect the distinct `$`-prefixed *keys* used in a filter (or
52
+ * any nested value) that are not in {@link SUPPORTED_QUERY_OPERATORS}.
53
+ *
54
+ * Only object *keys* are considered operators — matching the runtime
55
+ * convention (`walk-operation.ts`: `$`-prefixed keys are operators). String
56
+ * *values* like `$params.activeSince` are substitution references, never
57
+ * operators, so they are never flagged. `$and` / `$or` values are arrays of
58
+ * sub-filter objects and are walked recursively.
59
+ */
60
+ export function collectUnknownOperators(value) {
61
+ const unknown = new Set();
62
+ walk(value, unknown);
63
+ return [...unknown];
64
+ }
65
+ function walk(value, unknown) {
66
+ if (Array.isArray(value)) {
67
+ for (const item of value)
68
+ walk(item, unknown);
69
+ return;
70
+ }
71
+ if (value !== null && typeof value === "object") {
72
+ for (const [key, child] of Object.entries(value)) {
73
+ if (key.startsWith("$") && !SUPPORTED_QUERY_OPERATORS.has(key)) {
74
+ unknown.add(key);
75
+ }
76
+ walk(child, unknown);
77
+ }
78
+ }
79
+ }
80
+ //# sourceMappingURL=query-operators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-operators.js","sourceRoot":"","sources":["../../../src/lib/query-operators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,KAAK;IACL,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,SAAS;IACT,WAAW;IACX,aAAa;IACb,WAAW;IACX,eAAe;CAChB,CAAC;AAEF,mDAAmD;AACnD,MAAM,CAAC,MAAM,uBAAuB,GAAsB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE1E;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAwB,IAAI,GAAG,CAAC;IACpE,GAAG,qBAAqB;IACxB,GAAG,uBAAuB;CAC3B,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAU;IAChD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACrB,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,IAAI,CAAC,KAAU,EAAE,OAAoB;IAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO;IACT,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YACD,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Shared helper for refreshing the admin access token via the CLI auth API.
3
+ *
4
+ * Why this module exists:
5
+ *
6
+ * - `ApiClient.refreshToken()` already POSTs to /admin/api/auth/refresh
7
+ * before every API call when the access token is near expiry.
8
+ * - `primitive token` (issue #772) now needs the same exchange so it can
9
+ * auto-refresh a stale token before printing it.
10
+ * - `primitive login --token-stdin` does the same POST a third time to
11
+ * bootstrap credentials from a refresh token piped from another CLI.
12
+ *
13
+ * Putting the HTTP exchange + response parsing in one place keeps the
14
+ * response-shape contract in a single spot (server returns both `jwt` and
15
+ * `accessToken` — see `src/index.ts:2349-2356`) and means future server
16
+ * changes to that endpoint require touching exactly one CLI file.
17
+ *
18
+ * Design notes:
19
+ *
20
+ * - This module **does not** depend on `api-client.ts`. `ApiClient` imports
21
+ * it, not the other way around — that prevents the avoidable module cycle
22
+ * codex flagged in the design review.
23
+ * - It defines its own `RefreshError` so callers can map failures to
24
+ * whatever error type (or exit code) makes sense for their surface.
25
+ * `ApiClient` translates these to `ApiError`; the `token` command
26
+ * translates them to an `error()` + `process.exit(1)`.
27
+ * - It **does not save** the refreshed credentials. The caller decides
28
+ * whether to persist (the `token` command does; `ApiClient.refreshToken`
29
+ * does). Keeping persistence caller-owned matches the existing
30
+ * "storage is owned by `credentials-store.ts`" convention.
31
+ * - It **does not print anything**. Output (success/failure messaging) is
32
+ * the caller's responsibility — important because the `token` command
33
+ * must keep stdout clean for piping.
34
+ */
35
+ import type { Credentials } from "../types/index.js";
36
+ /**
37
+ * Reasons a refresh attempt can fail. `unauthorized` covers any HTTP non-2xx
38
+ * response (typically a 401 from an expired/invalid refresh token).
39
+ * `network` covers transport-level failures (DNS, ECONNREFUSED, etc.).
40
+ * `missing-refresh-token` is a precondition failure — there's no token to
41
+ * send.
42
+ */
43
+ export type RefreshFailureKind = "missing-refresh-token" | "unauthorized" | "network";
44
+ export declare class RefreshError extends Error {
45
+ kind: RefreshFailureKind;
46
+ statusCode?: number;
47
+ cause?: unknown;
48
+ constructor(message: string, kind: RefreshFailureKind, statusCode?: number, cause?: unknown);
49
+ }
50
+ /**
51
+ * Exchange the stored refresh token for a fresh `accessToken`. Returns a new
52
+ * `Credentials` object — the caller is responsible for persistence via
53
+ * `saveCredentials()` (or whatever storage the caller owns).
54
+ *
55
+ * Server contract: `POST {serverUrl}/admin/api/auth/refresh` with
56
+ * { refreshToken: string }
57
+ * Response (200):
58
+ * { jwt, accessToken, refreshToken, expiresAt }
59
+ *
60
+ * We accept `accessToken` as the canonical field and fall back to `jwt` for
61
+ * forward-compat (the server currently emits both — see issue #772 codex
62
+ * cleanup #3). Login's legacy `data.token` form is dropped — no server
63
+ * version that's still in support emits that field.
64
+ */
65
+ export declare function refreshAdminCredentials(credentials: Credentials): Promise<Credentials>;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Shared helper for refreshing the admin access token via the CLI auth API.
3
+ *
4
+ * Why this module exists:
5
+ *
6
+ * - `ApiClient.refreshToken()` already POSTs to /admin/api/auth/refresh
7
+ * before every API call when the access token is near expiry.
8
+ * - `primitive token` (issue #772) now needs the same exchange so it can
9
+ * auto-refresh a stale token before printing it.
10
+ * - `primitive login --token-stdin` does the same POST a third time to
11
+ * bootstrap credentials from a refresh token piped from another CLI.
12
+ *
13
+ * Putting the HTTP exchange + response parsing in one place keeps the
14
+ * response-shape contract in a single spot (server returns both `jwt` and
15
+ * `accessToken` — see `src/index.ts:2349-2356`) and means future server
16
+ * changes to that endpoint require touching exactly one CLI file.
17
+ *
18
+ * Design notes:
19
+ *
20
+ * - This module **does not** depend on `api-client.ts`. `ApiClient` imports
21
+ * it, not the other way around — that prevents the avoidable module cycle
22
+ * codex flagged in the design review.
23
+ * - It defines its own `RefreshError` so callers can map failures to
24
+ * whatever error type (or exit code) makes sense for their surface.
25
+ * `ApiClient` translates these to `ApiError`; the `token` command
26
+ * translates them to an `error()` + `process.exit(1)`.
27
+ * - It **does not save** the refreshed credentials. The caller decides
28
+ * whether to persist (the `token` command does; `ApiClient.refreshToken`
29
+ * does). Keeping persistence caller-owned matches the existing
30
+ * "storage is owned by `credentials-store.ts`" convention.
31
+ * - It **does not print anything**. Output (success/failure messaging) is
32
+ * the caller's responsibility — important because the `token` command
33
+ * must keep stdout clean for piping.
34
+ */
35
+ import { fetchWithTLS } from "./fetch.js";
36
+ export class RefreshError extends Error {
37
+ kind;
38
+ statusCode;
39
+ cause;
40
+ constructor(message, kind, statusCode, cause) {
41
+ super(message);
42
+ this.kind = kind;
43
+ this.statusCode = statusCode;
44
+ this.cause = cause;
45
+ this.name = "RefreshError";
46
+ }
47
+ }
48
+ /**
49
+ * Exchange the stored refresh token for a fresh `accessToken`. Returns a new
50
+ * `Credentials` object — the caller is responsible for persistence via
51
+ * `saveCredentials()` (or whatever storage the caller owns).
52
+ *
53
+ * Server contract: `POST {serverUrl}/admin/api/auth/refresh` with
54
+ * { refreshToken: string }
55
+ * Response (200):
56
+ * { jwt, accessToken, refreshToken, expiresAt }
57
+ *
58
+ * We accept `accessToken` as the canonical field and fall back to `jwt` for
59
+ * forward-compat (the server currently emits both — see issue #772 codex
60
+ * cleanup #3). Login's legacy `data.token` form is dropped — no server
61
+ * version that's still in support emits that field.
62
+ */
63
+ export async function refreshAdminCredentials(credentials) {
64
+ if (!credentials.refreshToken) {
65
+ throw new RefreshError("No refresh token available. Please login again.", "missing-refresh-token");
66
+ }
67
+ const url = `${credentials.serverUrl}/admin/api/auth/refresh`;
68
+ const headers = {
69
+ "Content-Type": "application/json",
70
+ };
71
+ if (credentials.globalAdminAppId) {
72
+ headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
73
+ }
74
+ let response;
75
+ try {
76
+ response = await fetchWithTLS(url, {
77
+ method: "POST",
78
+ headers,
79
+ body: JSON.stringify({ refreshToken: credentials.refreshToken }),
80
+ });
81
+ }
82
+ catch (cause) {
83
+ const reason = cause instanceof Error && cause.message
84
+ ? cause.message
85
+ : String(cause);
86
+ throw new RefreshError(`Network error refreshing token: ${reason}.`, "network", undefined, cause);
87
+ }
88
+ if (!response.ok) {
89
+ throw new RefreshError("Refresh token expired. Run 'primitive login'.", "unauthorized", response.status);
90
+ }
91
+ const data = (await response.json());
92
+ const accessToken = data?.accessToken || data?.jwt;
93
+ if (!accessToken) {
94
+ throw new RefreshError("Refresh response did not include an access token. Run 'primitive login'.", "unauthorized", response.status);
95
+ }
96
+ return {
97
+ ...credentials,
98
+ accessToken,
99
+ refreshToken: data.refreshToken || credentials.refreshToken,
100
+ expiresAt: data.expiresAt || new Date(Date.now() + 3600 * 1000).toISOString(),
101
+ };
102
+ }
103
+ //# sourceMappingURL=refresh-admin-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-admin-credentials.js","sourceRoot":"","sources":["../../../src/lib/refresh-admin-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAe1C,MAAM,OAAO,YAAa,SAAQ,KAAK;IAG5B;IACA;IACA;IAJT,YACE,OAAe,EACR,IAAwB,EACxB,UAAmB,EACnB,KAAe;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QAJR,SAAI,GAAJ,IAAI,CAAoB;QACxB,eAAU,GAAV,UAAU,CAAS;QACnB,UAAK,GAAL,KAAK,CAAU;QAGtB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAwB;IAExB,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;QAC9B,MAAM,IAAI,YAAY,CACpB,iDAAiD,EACjD,uBAAuB,CACxB,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,WAAW,CAAC,SAAS,yBAAyB,CAAC;IAC9D,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;KACnC,CAAC;IACF,IAAI,WAAW,CAAC,gBAAgB,EAAE,CAAC;QACjC,OAAO,CAAC,uBAAuB,CAAC,GAAG,WAAW,CAAC,gBAAgB,CAAC;IAClE,CAAC;IAED,IAAI,QAAkD,CAAC;IACvD,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE;YACjC,MAAM,EAAE,MAAM;YACd,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,WAAW,CAAC,YAAY,EAAE,CAAC;SACjE,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GACV,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO;YACrC,CAAC,CAAC,KAAK,CAAC,OAAO;YACf,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,IAAI,YAAY,CACpB,mCAAmC,MAAM,GAAG,EAC5C,SAAS,EACT,SAAS,EACT,KAAK,CACN,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CACpB,+CAA+C,EAC/C,cAAc,EACd,QAAQ,CAAC,MAAM,CAChB,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAQ,CAAC;IAC5C,MAAM,WAAW,GAAuB,IAAI,EAAE,WAAW,IAAI,IAAI,EAAE,GAAG,CAAC;IACvE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,YAAY,CACpB,0EAA0E,EAC1E,cAAc,EACd,QAAQ,CAAC,MAAM,CAChB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,GAAG,WAAW;QACd,WAAW;QACX,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,WAAW,CAAC,YAAY;QAC3D,SAAS,EACP,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;KACrE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Pure resolution of the target platform for `primitive init`.
3
+ *
4
+ * Background (issue #1009): `primitive init` must become platform-aware. The
5
+ * commander `-p, --platform` option no longer carries a `"web"` default, so an
6
+ * omitted flag is `undefined` and distinguishable from an explicit
7
+ * `--platform web`. Resolution follows a 4-case interactivity contract:
8
+ *
9
+ * (a) explicit `--platform` flag set -> use it (never prompt)
10
+ * (b) init-config TOML present (configPlatform may be set) -> use config
11
+ * value, else fall back to "web" (never prompt)
12
+ * (c) interactive TTY, no flag, no config -> prompt with `select`
13
+ * (d) non-TTY, no flag, no config -> fall back to "web" WITHOUT
14
+ * hanging and WITHOUT the `ERR_USE_AFTER_CLOSE` crash that
15
+ * `@inquirer/prompts` `select` throws on a closed/piped stdin
16
+ * (same hazard family hardened in #972/#999 — see `confirm-prompt.ts`).
17
+ *
18
+ * Extracting the decision into a pure function (with injectable `isTTY` /
19
+ * `promptFn`, mirroring `confirm-prompt.ts`) makes the otherwise
20
+ * terminal-dependent interactivity contract unit-testable.
21
+ */
22
+ export type Platform = "web" | "ios";
23
+ /** Prompt thunk returning the chosen platform. Injectable for testing. */
24
+ export type PlatformPromptFn = () => Promise<Platform>;
25
+ export interface ResolvePlatformOptions {
26
+ /** Value of the explicit `--platform` flag, or `undefined` when omitted. */
27
+ flag?: Platform;
28
+ /** `platform` key from a loaded `.primitive-init.toml`, if a config is present. */
29
+ configPlatform?: Platform;
30
+ /**
31
+ * Whether a non-interactive init-config file is in effect. When `true`, the
32
+ * prompt is skipped entirely (case b) regardless of TTY.
33
+ */
34
+ hasConfig: boolean;
35
+ /** Whether stdin is an interactive TTY. Inject `false` to exercise case (d). */
36
+ isTTY: boolean;
37
+ /** Prompt function used only in the interactive case (c). */
38
+ promptFn: PlatformPromptFn;
39
+ }
40
+ /**
41
+ * Resolve the target platform per the 4-case interactivity contract above.
42
+ *
43
+ * @returns the resolved platform (`"web"` or `"ios"`).
44
+ */
45
+ export declare function resolvePlatform(options: ResolvePlatformOptions): Promise<Platform>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Pure resolution of the target platform for `primitive init`.
3
+ *
4
+ * Background (issue #1009): `primitive init` must become platform-aware. The
5
+ * commander `-p, --platform` option no longer carries a `"web"` default, so an
6
+ * omitted flag is `undefined` and distinguishable from an explicit
7
+ * `--platform web`. Resolution follows a 4-case interactivity contract:
8
+ *
9
+ * (a) explicit `--platform` flag set -> use it (never prompt)
10
+ * (b) init-config TOML present (configPlatform may be set) -> use config
11
+ * value, else fall back to "web" (never prompt)
12
+ * (c) interactive TTY, no flag, no config -> prompt with `select`
13
+ * (d) non-TTY, no flag, no config -> fall back to "web" WITHOUT
14
+ * hanging and WITHOUT the `ERR_USE_AFTER_CLOSE` crash that
15
+ * `@inquirer/prompts` `select` throws on a closed/piped stdin
16
+ * (same hazard family hardened in #972/#999 — see `confirm-prompt.ts`).
17
+ *
18
+ * Extracting the decision into a pure function (with injectable `isTTY` /
19
+ * `promptFn`, mirroring `confirm-prompt.ts`) makes the otherwise
20
+ * terminal-dependent interactivity contract unit-testable.
21
+ */
22
+ /**
23
+ * Resolve the target platform per the 4-case interactivity contract above.
24
+ *
25
+ * @returns the resolved platform (`"web"` or `"ios"`).
26
+ */
27
+ export async function resolvePlatform(options) {
28
+ // (a) explicit flag wins, no prompt.
29
+ if (options.flag !== undefined) {
30
+ return options.flag;
31
+ }
32
+ // (b) non-interactive config present: use its platform or default to web.
33
+ if (options.hasConfig) {
34
+ return options.configPlatform ?? "web";
35
+ }
36
+ // (c) interactive TTY, no flag, no config: prompt.
37
+ if (options.isTTY) {
38
+ return options.promptFn();
39
+ }
40
+ // (d) non-TTY, no flag, no config: never prompt; fall back to web.
41
+ return "web";
42
+ }
43
+ //# sourceMappingURL=resolve-platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-platform.js","sourceRoot":"","sources":["../../../src/lib/resolve-platform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAuBH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAA+B;IAE/B,qCAAqC;IACrC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC;IAED,0EAA0E;IAC1E,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IACzC,CAAC;IAED,mDAAmD;IACnD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAED,mEAAmE;IACnE,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Returns true if the skill is currently installed.
3
+ */
4
+ export declare function isSkillInstalled(): boolean;
5
+ /**
6
+ * Auto-update an already-installed skill if a newer version is bundled.
7
+ * Called after command execution for non-init, non-skill commands.
8
+ * If the skill is not installed, shows a non-blocking hint instead.
9
+ * Never throws — fails silently like version-check.
10
+ */
11
+ export declare function checkSkillStatus(): Promise<void>;
12
+ /**
13
+ * Explicitly install or reinstall the skill. Shows output.
14
+ */
15
+ export declare function installSkillExplicit(): boolean;
16
+ /**
17
+ * Uninstall the skill.
18
+ */
19
+ export declare function uninstallSkill(): boolean;
20
+ /**
21
+ * Show current skill installation status.
22
+ */
23
+ export declare function skillStatus(): void;