@principal-ai/principal-studio-cli 0.35.3

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 (184) hide show
  1. package/README.md +172 -0
  2. package/dist/commands/agent-session.d.ts +3 -0
  3. package/dist/commands/agent-session.d.ts.map +1 -0
  4. package/dist/commands/agent-session.js +67 -0
  5. package/dist/commands/agent-sessions.d.ts +11 -0
  6. package/dist/commands/agent-sessions.d.ts.map +1 -0
  7. package/dist/commands/agent-sessions.js +39 -0
  8. package/dist/commands/auxiliary/index.d.ts +3 -0
  9. package/dist/commands/auxiliary/index.d.ts.map +1 -0
  10. package/dist/commands/auxiliary/index.js +9 -0
  11. package/dist/commands/auxiliary/validate.d.ts +3 -0
  12. package/dist/commands/auxiliary/validate.d.ts.map +1 -0
  13. package/dist/commands/auxiliary/validate.js +123 -0
  14. package/dist/commands/collector/check.d.ts +6 -0
  15. package/dist/commands/collector/check.d.ts.map +1 -0
  16. package/dist/commands/collector/check.js +153 -0
  17. package/dist/commands/collector/diagnose.d.ts +6 -0
  18. package/dist/commands/collector/diagnose.d.ts.map +1 -0
  19. package/dist/commands/collector/diagnose.js +229 -0
  20. package/dist/commands/collector/index.d.ts +6 -0
  21. package/dist/commands/collector/index.d.ts.map +1 -0
  22. package/dist/commands/collector/index.js +16 -0
  23. package/dist/commands/collector/status.d.ts +6 -0
  24. package/dist/commands/collector/status.d.ts.map +1 -0
  25. package/dist/commands/collector/status.js +77 -0
  26. package/dist/commands/collector/utils.d.ts +92 -0
  27. package/dist/commands/collector/utils.d.ts.map +1 -0
  28. package/dist/commands/collector/utils.js +154 -0
  29. package/dist/commands/coverage.d.ts +9 -0
  30. package/dist/commands/coverage.d.ts.map +1 -0
  31. package/dist/commands/coverage.js +252 -0
  32. package/dist/commands/create.d.ts +6 -0
  33. package/dist/commands/create.d.ts.map +1 -0
  34. package/dist/commands/create.js +50 -0
  35. package/dist/commands/events/index.d.ts +3 -0
  36. package/dist/commands/events/index.d.ts.map +1 -0
  37. package/dist/commands/events/index.js +9 -0
  38. package/dist/commands/events/validate.d.ts +3 -0
  39. package/dist/commands/events/validate.d.ts.map +1 -0
  40. package/dist/commands/events/validate.js +124 -0
  41. package/dist/commands/formats.d.ts +6 -0
  42. package/dist/commands/formats.d.ts.map +1 -0
  43. package/dist/commands/formats.js +1118 -0
  44. package/dist/commands/hooks.d.ts +9 -0
  45. package/dist/commands/hooks.d.ts.map +1 -0
  46. package/dist/commands/hooks.js +295 -0
  47. package/dist/commands/inbox.d.ts +13 -0
  48. package/dist/commands/inbox.d.ts.map +1 -0
  49. package/dist/commands/inbox.js +179 -0
  50. package/dist/commands/init.d.ts +6 -0
  51. package/dist/commands/init.d.ts.map +1 -0
  52. package/dist/commands/init.js +220 -0
  53. package/dist/commands/lint.d.ts +11 -0
  54. package/dist/commands/lint.d.ts.map +1 -0
  55. package/dist/commands/lint.js +71 -0
  56. package/dist/commands/list.d.ts +6 -0
  57. package/dist/commands/list.d.ts.map +1 -0
  58. package/dist/commands/list.js +78 -0
  59. package/dist/commands/migrate-nodes.d.ts +9 -0
  60. package/dist/commands/migrate-nodes.d.ts.map +1 -0
  61. package/dist/commands/migrate-nodes.js +363 -0
  62. package/dist/commands/migrate-scopes-to-canvas.d.ts +3 -0
  63. package/dist/commands/migrate-scopes-to-canvas.d.ts.map +1 -0
  64. package/dist/commands/migrate-scopes-to-canvas.js +143 -0
  65. package/dist/commands/migration.d.ts +6 -0
  66. package/dist/commands/migration.d.ts.map +1 -0
  67. package/dist/commands/migration.js +247 -0
  68. package/dist/commands/open-studio.d.ts +10 -0
  69. package/dist/commands/open-studio.d.ts.map +1 -0
  70. package/dist/commands/open-studio.js +32 -0
  71. package/dist/commands/opencode/fetch.d.ts +3 -0
  72. package/dist/commands/opencode/fetch.d.ts.map +1 -0
  73. package/dist/commands/opencode/fetch.js +25 -0
  74. package/dist/commands/opencode/index.d.ts +3 -0
  75. package/dist/commands/opencode/index.d.ts.map +1 -0
  76. package/dist/commands/opencode/index.js +11 -0
  77. package/dist/commands/opencode/list-sessions.d.ts +3 -0
  78. package/dist/commands/opencode/list-sessions.d.ts.map +1 -0
  79. package/dist/commands/opencode/list-sessions.js +20 -0
  80. package/dist/commands/repo.d.ts +18 -0
  81. package/dist/commands/repo.d.ts.map +1 -0
  82. package/dist/commands/repo.js +233 -0
  83. package/dist/commands/schema.d.ts +6 -0
  84. package/dist/commands/schema.d.ts.map +1 -0
  85. package/dist/commands/schema.js +367 -0
  86. package/dist/commands/scopes/index.d.ts +3 -0
  87. package/dist/commands/scopes/index.d.ts.map +1 -0
  88. package/dist/commands/scopes/index.js +9 -0
  89. package/dist/commands/scopes/validate.d.ts +3 -0
  90. package/dist/commands/scopes/validate.d.ts.map +1 -0
  91. package/dist/commands/scopes/validate.js +187 -0
  92. package/dist/commands/setup.d.ts +6 -0
  93. package/dist/commands/setup.d.ts.map +1 -0
  94. package/dist/commands/setup.js +406 -0
  95. package/dist/commands/starred-collections.d.ts +12 -0
  96. package/dist/commands/starred-collections.d.ts.map +1 -0
  97. package/dist/commands/starred-collections.js +310 -0
  98. package/dist/commands/subsystem-model.d.ts +11 -0
  99. package/dist/commands/subsystem-model.d.ts.map +1 -0
  100. package/dist/commands/subsystem-model.js +249 -0
  101. package/dist/commands/topic.d.ts +15 -0
  102. package/dist/commands/topic.d.ts.map +1 -0
  103. package/dist/commands/topic.js +683 -0
  104. package/dist/commands/tour.d.ts +23 -0
  105. package/dist/commands/tour.d.ts.map +1 -0
  106. package/dist/commands/tour.js +783 -0
  107. package/dist/commands/trace/flow.d.ts +6 -0
  108. package/dist/commands/trace/flow.d.ts.map +1 -0
  109. package/dist/commands/trace/flow.js +158 -0
  110. package/dist/commands/trace/index.d.ts +6 -0
  111. package/dist/commands/trace/index.d.ts.map +1 -0
  112. package/dist/commands/trace/index.js +20 -0
  113. package/dist/commands/trace/inspect.d.ts +6 -0
  114. package/dist/commands/trace/inspect.d.ts.map +1 -0
  115. package/dist/commands/trace/inspect.js +187 -0
  116. package/dist/commands/trace/list.d.ts +6 -0
  117. package/dist/commands/trace/list.d.ts.map +1 -0
  118. package/dist/commands/trace/list.js +145 -0
  119. package/dist/commands/trace/registrations.d.ts +6 -0
  120. package/dist/commands/trace/registrations.d.ts.map +1 -0
  121. package/dist/commands/trace/registrations.js +81 -0
  122. package/dist/commands/trace/validate.d.ts +6 -0
  123. package/dist/commands/trace/validate.d.ts.map +1 -0
  124. package/dist/commands/trace/validate.js +276 -0
  125. package/dist/commands/trail.d.ts +36 -0
  126. package/dist/commands/trail.d.ts.map +1 -0
  127. package/dist/commands/trail.js +622 -0
  128. package/dist/commands/validate.d.ts +12 -0
  129. package/dist/commands/validate.d.ts.map +1 -0
  130. package/dist/commands/validate.js +3334 -0
  131. package/dist/commands/workflow/implementation.d.ts +3 -0
  132. package/dist/commands/workflow/implementation.d.ts.map +1 -0
  133. package/dist/commands/workflow/implementation.js +51 -0
  134. package/dist/commands/workflow/index.d.ts +3 -0
  135. package/dist/commands/workflow/index.d.ts.map +1 -0
  136. package/dist/commands/workflow/index.js +19 -0
  137. package/dist/commands/workflow/inspect.d.ts +3 -0
  138. package/dist/commands/workflow/inspect.d.ts.map +1 -0
  139. package/dist/commands/workflow/inspect.js +123 -0
  140. package/dist/commands/workflow/list.d.ts +3 -0
  141. package/dist/commands/workflow/list.d.ts.map +1 -0
  142. package/dist/commands/workflow/list.js +95 -0
  143. package/dist/commands/workflow/render.d.ts +3 -0
  144. package/dist/commands/workflow/render.d.ts.map +1 -0
  145. package/dist/commands/workflow/render.js +88 -0
  146. package/dist/commands/workflow/test.d.ts +3 -0
  147. package/dist/commands/workflow/test.d.ts.map +1 -0
  148. package/dist/commands/workflow/test.js +141 -0
  149. package/dist/commands/workflow/utils.d.ts +49 -0
  150. package/dist/commands/workflow/utils.d.ts.map +1 -0
  151. package/dist/commands/workflow/utils.js +164 -0
  152. package/dist/commands/workflow/validate.d.ts +3 -0
  153. package/dist/commands/workflow/validate.d.ts.map +1 -0
  154. package/dist/commands/workflow/validate.js +344 -0
  155. package/dist/file-utils.d.ts +15 -0
  156. package/dist/file-utils.d.ts.map +1 -0
  157. package/dist/file-utils.js +29 -0
  158. package/dist/index.cjs +285718 -0
  159. package/dist/index.cjs.map +7 -0
  160. package/dist/index.d.ts +8 -0
  161. package/dist/index.d.ts.map +1 -0
  162. package/dist/index.js +81 -0
  163. package/dist/lib/bridge-ipc.d.ts +29 -0
  164. package/dist/lib/bridge-ipc.d.ts.map +1 -0
  165. package/dist/lib/bridge-ipc.js +128 -0
  166. package/dist/lib/github-user.d.ts +17 -0
  167. package/dist/lib/github-user.d.ts.map +1 -0
  168. package/dist/lib/github-user.js +53 -0
  169. package/dist/lib/open-url.d.ts +11 -0
  170. package/dist/lib/open-url.d.ts.map +1 -0
  171. package/dist/lib/open-url.js +45 -0
  172. package/dist/lib/subsystem-model-store.d.ts +74 -0
  173. package/dist/lib/subsystem-model-store.d.ts.map +1 -0
  174. package/dist/lib/subsystem-model-store.js +292 -0
  175. package/dist/lib/tour-cache.d.ts +26 -0
  176. package/dist/lib/tour-cache.d.ts.map +1 -0
  177. package/dist/lib/tour-cache.js +46 -0
  178. package/dist/lib/trail-cache.d.ts +40 -0
  179. package/dist/lib/trail-cache.d.ts.map +1 -0
  180. package/dist/lib/trail-cache.js +97 -0
  181. package/dist/lib/viewer-ipc.d.ts +44 -0
  182. package/dist/lib/viewer-ipc.d.ts.map +1 -0
  183. package/dist/lib/viewer-ipc.js +68 -0
  184. package/package.json +68 -0
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Starred-collections command — manage starred-repo collections on web-ade.
3
+ *
4
+ * Collections are per-user curated groups of repos (with cached descriptions,
5
+ * star counts, and avatars). Each collection is owned by a user or an org.
6
+ *
7
+ * Resolves a GitHub token locally (gh CLI → git credential helper) and calls
8
+ * the web-ade starred-collections API with `Authorization: Bearer <token>`.
9
+ */
10
+ import { Command } from 'commander';
11
+ import { spawnSync } from 'node:child_process';
12
+ const BASE_URL = process.env.PRINCIPAL_ADE_BASE_URL?.replace(/\/+$/, '') ||
13
+ 'https://app.principal-ade.com';
14
+ // ============================================================================
15
+ // Auth
16
+ // ============================================================================
17
+ function resolveTokenViaGh() {
18
+ const result = spawnSync('gh', ['auth', 'token'], {
19
+ encoding: 'utf8',
20
+ stdio: ['ignore', 'pipe', 'ignore'],
21
+ });
22
+ if (result.status === 0 && result.stdout) {
23
+ const token = result.stdout.trim();
24
+ if (token)
25
+ return token;
26
+ }
27
+ return null;
28
+ }
29
+ function resolveTokenViaGitCredential() {
30
+ const result = spawnSync('git', ['credential', 'fill'], {
31
+ encoding: 'utf8',
32
+ input: 'protocol=https\nhost=github.com\n\n',
33
+ stdio: ['pipe', 'pipe', 'ignore'],
34
+ });
35
+ if (result.status !== 0 || !result.stdout)
36
+ return null;
37
+ for (const line of result.stdout.split('\n')) {
38
+ if (line.startsWith('password=')) {
39
+ const token = line.slice('password='.length).trim();
40
+ if (token)
41
+ return token;
42
+ }
43
+ }
44
+ return null;
45
+ }
46
+ function resolveToken() {
47
+ return resolveTokenViaGh() ?? resolveTokenViaGitCredential();
48
+ }
49
+ function exitWithTokenError() {
50
+ process.stderr.write('Could not resolve a GitHub token. Run `gh auth login`, or configure a git credential helper for github.com.\n');
51
+ process.exit(2);
52
+ }
53
+ // ============================================================================
54
+ // HTTP helpers
55
+ // ============================================================================
56
+ async function describeHttpError(response) {
57
+ let serverMessage = '';
58
+ let code = '';
59
+ try {
60
+ const body = (await response.clone().json());
61
+ serverMessage = body.error ?? '';
62
+ code = body.code ?? '';
63
+ }
64
+ catch {
65
+ // body wasn't JSON — fall through to status-only message
66
+ }
67
+ const fallback = response.status === 404
68
+ ? 'Collection not found'
69
+ : response.status === 403
70
+ ? 'Not authorized'
71
+ : response.status === 401
72
+ ? 'GitHub token rejected'
73
+ : response.status === 409
74
+ ? 'Conflict (duplicate name or repo)'
75
+ : `HTTP ${response.status}`;
76
+ const human = serverMessage || fallback;
77
+ return `${human}${code ? ` [${code}]` : ''}`;
78
+ }
79
+ // ============================================================================
80
+ // Subcommands
81
+ // ============================================================================
82
+ async function listCollections() {
83
+ const token = resolveToken();
84
+ if (!token)
85
+ exitWithTokenError();
86
+ let response;
87
+ try {
88
+ response = await fetch(`${BASE_URL}/api/starred-collections?include_items=false`, {
89
+ headers: {
90
+ Authorization: `Bearer ${token}`,
91
+ Accept: 'application/json',
92
+ },
93
+ });
94
+ }
95
+ catch (err) {
96
+ process.stderr.write(`Network error listing collections: ${err.message}\n`);
97
+ process.exit(1);
98
+ }
99
+ if (!response.ok) {
100
+ process.stderr.write(`${await describeHttpError(response)}\n`);
101
+ process.exit(1);
102
+ }
103
+ const body = (await response.json());
104
+ process.stdout.write(JSON.stringify(body.collections, null, 2));
105
+ process.stdout.write('\n');
106
+ }
107
+ async function createCollection(name, options) {
108
+ const token = resolveToken();
109
+ if (!token)
110
+ exitWithTokenError();
111
+ const payload = { name };
112
+ if (options.description)
113
+ payload.description = options.description;
114
+ if (options.icon)
115
+ payload.icon = options.icon;
116
+ if (options.orgLogin)
117
+ payload.orgLogin = options.orgLogin;
118
+ let response;
119
+ try {
120
+ response = await fetch(`${BASE_URL}/api/starred-collections`, {
121
+ method: 'POST',
122
+ headers: {
123
+ Authorization: `Bearer ${token}`,
124
+ 'Content-Type': 'application/json',
125
+ Accept: 'application/json',
126
+ },
127
+ body: JSON.stringify(payload),
128
+ });
129
+ }
130
+ catch (err) {
131
+ process.stderr.write(`Network error creating collection: ${err.message}\n`);
132
+ process.exit(1);
133
+ }
134
+ if (!response.ok) {
135
+ process.stderr.write(`${await describeHttpError(response)}\n`);
136
+ process.exit(1);
137
+ }
138
+ const collection = (await response.json());
139
+ process.stdout.write(JSON.stringify(collection, null, 2));
140
+ process.stdout.write('\n');
141
+ }
142
+ async function listRepos(collectionId) {
143
+ const token = resolveToken();
144
+ if (!token)
145
+ exitWithTokenError();
146
+ let response;
147
+ try {
148
+ response = await fetch(`${BASE_URL}/api/starred-collections/${encodeURIComponent(collectionId)}`, {
149
+ headers: {
150
+ Authorization: `Bearer ${token}`,
151
+ Accept: 'application/json',
152
+ },
153
+ });
154
+ }
155
+ catch (err) {
156
+ process.stderr.write(`Network error fetching collection: ${err.message}\n`);
157
+ process.exit(1);
158
+ }
159
+ if (!response.ok) {
160
+ process.stderr.write(`${await describeHttpError(response)}\n`);
161
+ process.exit(1);
162
+ }
163
+ const collection = (await response.json());
164
+ process.stdout.write(JSON.stringify(collection.repos, null, 2));
165
+ process.stdout.write('\n');
166
+ }
167
+ async function addRepo(collectionId, ownerRepo) {
168
+ const slashIndex = ownerRepo.indexOf('/');
169
+ if (slashIndex === -1 || slashIndex === 0 || slashIndex === ownerRepo.length - 1) {
170
+ process.stderr.write(`Invalid repo format: "${ownerRepo}". Use <owner>/<repo> (e.g. "principal-ai/web-ade").\n`);
171
+ process.exit(2);
172
+ }
173
+ const owner = ownerRepo.slice(0, slashIndex);
174
+ const repo = ownerRepo.slice(slashIndex + 1);
175
+ const token = resolveToken();
176
+ if (!token)
177
+ exitWithTokenError();
178
+ let response;
179
+ try {
180
+ response = await fetch(`${BASE_URL}/api/starred-collections/${encodeURIComponent(collectionId)}/repos`, {
181
+ method: 'POST',
182
+ headers: {
183
+ Authorization: `Bearer ${token}`,
184
+ 'Content-Type': 'application/json',
185
+ Accept: 'application/json',
186
+ },
187
+ body: JSON.stringify({ owner, repo }),
188
+ });
189
+ }
190
+ catch (err) {
191
+ process.stderr.write(`Network error adding repo: ${err.message}\n`);
192
+ process.exit(1);
193
+ }
194
+ if (!response.ok) {
195
+ process.stderr.write(`${await describeHttpError(response)}\n`);
196
+ process.exit(1);
197
+ }
198
+ const repoEntry = (await response.json());
199
+ process.stdout.write(JSON.stringify(repoEntry, null, 2));
200
+ process.stdout.write('\n');
201
+ }
202
+ async function removeRepo(collectionId, ownerRepo) {
203
+ const slashIndex = ownerRepo.indexOf('/');
204
+ if (slashIndex === -1 || slashIndex === 0 || slashIndex === ownerRepo.length - 1) {
205
+ process.stderr.write(`Invalid repo format: "${ownerRepo}". Use <owner>/<repo> (e.g. "principal-ai/web-ade").\n`);
206
+ process.exit(2);
207
+ }
208
+ const owner = ownerRepo.slice(0, slashIndex);
209
+ const repo = ownerRepo.slice(slashIndex + 1);
210
+ const token = resolveToken();
211
+ if (!token)
212
+ exitWithTokenError();
213
+ let response;
214
+ try {
215
+ response = await fetch(`${BASE_URL}/api/starred-collections/${encodeURIComponent(collectionId)}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`, {
216
+ method: 'DELETE',
217
+ headers: {
218
+ Authorization: `Bearer ${token}`,
219
+ Accept: 'application/json',
220
+ },
221
+ });
222
+ }
223
+ catch (err) {
224
+ process.stderr.write(`Network error removing repo: ${err.message}\n`);
225
+ process.exit(1);
226
+ }
227
+ if (!response.ok && response.status !== 204) {
228
+ process.stderr.write(`${await describeHttpError(response)}\n`);
229
+ process.exit(1);
230
+ }
231
+ process.stdout.write(`Removed ${owner}/${repo} from collection ${collectionId}\n`);
232
+ }
233
+ async function deleteCollection(collectionId) {
234
+ const token = resolveToken();
235
+ if (!token)
236
+ exitWithTokenError();
237
+ let response;
238
+ try {
239
+ response = await fetch(`${BASE_URL}/api/starred-collections/${encodeURIComponent(collectionId)}`, {
240
+ method: 'DELETE',
241
+ headers: {
242
+ Authorization: `Bearer ${token}`,
243
+ Accept: 'application/json',
244
+ },
245
+ });
246
+ }
247
+ catch (err) {
248
+ process.stderr.write(`Network error deleting collection: ${err.message}\n`);
249
+ process.exit(1);
250
+ }
251
+ if (!response.ok && response.status !== 204) {
252
+ process.stderr.write(`${await describeHttpError(response)}\n`);
253
+ process.exit(1);
254
+ }
255
+ process.stdout.write(`Deleted collection ${collectionId}\n`);
256
+ }
257
+ // ============================================================================
258
+ // Command wiring
259
+ // ============================================================================
260
+ export function createStarredCollectionsCommand() {
261
+ const command = new Command('starred-collections');
262
+ command.description('Manage starred-repo collections on web-ade');
263
+ command
264
+ .command('list')
265
+ .description('List all collections for the authenticated user (including org collections)')
266
+ .action(async () => {
267
+ await listCollections();
268
+ });
269
+ command
270
+ .command('create')
271
+ .description('Create a new collection')
272
+ .argument('<name>', 'Collection name (1-100 chars)')
273
+ .option('--description <text>', 'Optional description')
274
+ .option('--icon <name>', 'Lucide icon name (e.g. Star, Sparkles, Rocket, Heart)')
275
+ .option('--org-login <login>', 'Create as an org-owned collection (must be a member)')
276
+ .action(async (name, options) => {
277
+ await createCollection(name, options);
278
+ });
279
+ command
280
+ .command('repos')
281
+ .description('List all repos in a collection')
282
+ .argument('<collection-id>', 'Collection id')
283
+ .action(async (collectionId) => {
284
+ await listRepos(collectionId);
285
+ });
286
+ command
287
+ .command('add')
288
+ .description('Add a repo to a collection (auto-fetches description, stars, avatar from GitHub)')
289
+ .argument('<collection-id>', 'Collection id')
290
+ .argument('<owner/repo>', 'Repo in owner/repo format (e.g. principal-ai/web-ade)')
291
+ .action(async (collectionId, ownerRepo) => {
292
+ await addRepo(collectionId, ownerRepo);
293
+ });
294
+ command
295
+ .command('remove')
296
+ .description('Remove a repo from a collection')
297
+ .argument('<collection-id>', 'Collection id')
298
+ .argument('<owner/repo>', 'Repo in owner/repo format (e.g. principal-ai/web-ade)')
299
+ .action(async (collectionId, ownerRepo) => {
300
+ await removeRepo(collectionId, ownerRepo);
301
+ });
302
+ command
303
+ .command('delete')
304
+ .description('Delete a collection')
305
+ .argument('<collection-id>', 'Collection id')
306
+ .action(async (collectionId) => {
307
+ await deleteCollection(collectionId);
308
+ });
309
+ return command;
310
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `principal-ai subsystem-model` — create / open / list subsystem models
3
+ * without requiring Principal Studio's HTTP bridge to already be running.
4
+ *
5
+ * Create validates + persists to `~/.principal/subsystem-models/` (same store
6
+ * Studio uses), then opens the model: IPC handoff if Studio is up, otherwise
7
+ * launches Studio with `SUBSYSTEM_MODEL_ID` so it opens the new tab on boot.
8
+ */
9
+ import { Command } from 'commander';
10
+ export declare function createSubsystemModelCommand(): Command;
11
+ //# sourceMappingURL=subsystem-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subsystem-model.d.ts","sourceRoot":"","sources":["../../src/commands/subsystem-model.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4QpC,wBAAgB,2BAA2B,IAAI,OAAO,CAwCrD"}
@@ -0,0 +1,249 @@
1
+ /**
2
+ * `principal-ai subsystem-model` — create / open / list subsystem models
3
+ * without requiring Principal Studio's HTTP bridge to already be running.
4
+ *
5
+ * Create validates + persists to `~/.principal/subsystem-models/` (same store
6
+ * Studio uses), then opens the model: IPC handoff if Studio is up, otherwise
7
+ * launches Studio with `SUBSYSTEM_MODEL_ID` so it opens the new tab on boot.
8
+ */
9
+ import { spawn } from 'node:child_process';
10
+ import { readFileSync } from 'node:fs';
11
+ import { Command } from 'commander';
12
+ import { tryResolveViewerLaunch } from './trail.js';
13
+ import { handoffToRunning } from '../lib/viewer-ipc.js';
14
+ import { createSubsystemModel, findCreateProblems, getSubsystemModel, isRepoRoots, listSubsystemModels, subsystemModelFilePath, } from '../lib/subsystem-model-store.js';
15
+ async function readPayloadFromStdin() {
16
+ const chunks = [];
17
+ for await (const chunk of process.stdin) {
18
+ chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
19
+ }
20
+ return Buffer.concat(chunks).toString('utf8');
21
+ }
22
+ async function readPayload(file) {
23
+ const useStdin = !file || file === '-';
24
+ const raw = useStdin ? await readPayloadFromStdin() : readFileSync(file, 'utf8');
25
+ if (!raw.trim()) {
26
+ process.stderr.write('Empty payload\n');
27
+ process.exit(2);
28
+ }
29
+ try {
30
+ return JSON.parse(raw);
31
+ }
32
+ catch (err) {
33
+ process.stderr.write(`Could not parse payload as JSON: ${err.message}\n`);
34
+ process.exit(2);
35
+ }
36
+ }
37
+ function studioHttpBase() {
38
+ const port = process.env['PRINCIPAL_STUDIO_HTTP_PORT'] ?? '3045';
39
+ return `http://127.0.0.1:${port}`;
40
+ }
41
+ /** True when Studio's HTTP bridge answers /health. */
42
+ async function studioHttpUp() {
43
+ try {
44
+ const res = await fetch(`${studioHttpBase()}/health`, {
45
+ signal: AbortSignal.timeout(400),
46
+ });
47
+ return res.ok;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ /**
54
+ * Prefer Studio's HTTP create when the bridge is up — that path runs full
55
+ * file/symbol verification. Falls back to null when unreachable so the CLI
56
+ * can persist locally.
57
+ */
58
+ async function createViaHttp(body) {
59
+ try {
60
+ const res = await fetch(`${studioHttpBase()}/api/subsystem-model`, {
61
+ method: 'POST',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify(body),
64
+ signal: AbortSignal.timeout(30_000),
65
+ });
66
+ const json = (await res.json());
67
+ if (!res.ok || !json.ok || !json.graph) {
68
+ process.stderr.write(`Studio rejected model: ${json.error ?? `HTTP ${res.status}`}\n`);
69
+ process.exit(2);
70
+ }
71
+ return json.graph;
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ }
77
+ async function openViaHttp(id) {
78
+ try {
79
+ const res = await fetch(`${studioHttpBase()}/api/subsystem-model/open`, {
80
+ method: 'POST',
81
+ headers: { 'Content-Type': 'application/json' },
82
+ body: JSON.stringify({ id }),
83
+ signal: AbortSignal.timeout(5_000),
84
+ });
85
+ const json = (await res.json());
86
+ return res.ok && json.ok === true;
87
+ }
88
+ catch {
89
+ return false;
90
+ }
91
+ }
92
+ /**
93
+ * Open a stored model in Studio. Order: HTTP /open → IPC → spawn Studio.
94
+ * Returns true when handed off to a running instance (caller can exit).
95
+ * Returns false when it spawned Studio (caller should wait on the child).
96
+ */
97
+ async function openModel(id, options) {
98
+ if (await openViaHttp(id))
99
+ return 'handed';
100
+ if (await handoffToRunning({ kind: 'LOAD_SUBSYSTEM_GRAPH', graphId: id })) {
101
+ return 'handed';
102
+ }
103
+ const resolved = tryResolveViewerLaunch(options.viewerDir);
104
+ if (!resolved.ok) {
105
+ process.stderr.write(`${resolved.error}\n`);
106
+ return 'failed';
107
+ }
108
+ const launch = resolved.launch;
109
+ const env = {
110
+ ...process.env,
111
+ SUBSYSTEM_MODEL_ID: id,
112
+ };
113
+ process.stderr.write(`Launching Principal Studio for ${id}…\n`);
114
+ const child = launch.kind === 'installed'
115
+ ? spawn(launch.bin, [], { env, stdio: 'inherit' })
116
+ : spawn('bun', ['start'], { cwd: launch.dir, env, stdio: 'inherit' });
117
+ if (!options.waitOnSpawn) {
118
+ child.unref();
119
+ return 'spawned';
120
+ }
121
+ return await new Promise((resolve) => {
122
+ child.on('error', (err) => {
123
+ process.stderr.write(`Failed to launch Principal Studio: ${err.message}\n`);
124
+ resolve('failed');
125
+ });
126
+ child.on('exit', (code) => {
127
+ if (code && code !== 0) {
128
+ process.exit(code);
129
+ }
130
+ resolve('spawned');
131
+ });
132
+ });
133
+ }
134
+ async function createAction(options) {
135
+ const parsed = await readPayload(options.file);
136
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
137
+ process.stderr.write('Payload must be a JSON object\n');
138
+ process.exit(2);
139
+ }
140
+ const body = parsed;
141
+ const problems = findCreateProblems(body);
142
+ if (problems.length > 0) {
143
+ process.stderr.write(`invalid model: ${problems.join('; ')}\n`);
144
+ process.exit(2);
145
+ }
146
+ let record = null;
147
+ if (await studioHttpUp()) {
148
+ record = await createViaHttp(body);
149
+ }
150
+ if (!record) {
151
+ record = await createSubsystemModel({
152
+ title: body['title'],
153
+ description: typeof body['description'] === 'string' ? body['description'] : undefined,
154
+ components: body['components'],
155
+ edges: body['edges'],
156
+ throughlines: Array.isArray(body['throughlines']) ? body['throughlines'] : undefined,
157
+ source: typeof body['source'] === 'string' ? body['source'] : undefined,
158
+ repo: body['repo'],
159
+ repoRoot: typeof body['repoRoot'] === 'string' ? body['repoRoot'] : undefined,
160
+ repoRoots: isRepoRoots(body['repoRoots']) ? body['repoRoots'] : undefined,
161
+ });
162
+ }
163
+ process.stderr.write(`Created subsystem model ${record.id} → ${subsystemModelFilePath(record.id)}\n`);
164
+ // Commander `--no-open` flips `open` to false (default true).
165
+ if (options.open !== false) {
166
+ const outcome = await openModel(record.id, {
167
+ viewerDir: options.viewerDir,
168
+ waitOnSpawn: false,
169
+ });
170
+ if (outcome === 'handed') {
171
+ process.stderr.write(`Opened in Principal Studio: ${record.id}\n`);
172
+ }
173
+ else if (outcome === 'spawned') {
174
+ process.stderr.write(`Principal Studio launching with model ${record.id}\n`);
175
+ }
176
+ else {
177
+ process.stderr.write(`Model saved but could not open Principal Studio. Run \`principal-ai open-studio\` or \`principal-ai subsystem-model open ${record.id}\`.\n`);
178
+ }
179
+ }
180
+ process.stdout.write(JSON.stringify({ ok: true, graph: record }, null, 2) + '\n');
181
+ }
182
+ async function openAction(id, options) {
183
+ if (!id) {
184
+ process.stderr.write('Pass a model id.\n');
185
+ process.exit(2);
186
+ }
187
+ const existing = await getSubsystemModel(id);
188
+ if (!existing) {
189
+ process.stderr.write(`Model not found: ${id}\n`);
190
+ process.exit(2);
191
+ }
192
+ const outcome = await openModel(id, { viewerDir: options.viewerDir, waitOnSpawn: true });
193
+ if (outcome === 'handed') {
194
+ process.stderr.write(`Opened in Principal Studio: ${id}\n`);
195
+ return;
196
+ }
197
+ if (outcome === 'failed') {
198
+ process.stderr.write(`Could not open Principal Studio. Is @principal-ai/subsystems-studio installed?\n`);
199
+ process.exit(2);
200
+ }
201
+ }
202
+ async function listAction() {
203
+ const graphs = await listSubsystemModels();
204
+ process.stdout.write(JSON.stringify({
205
+ ok: true,
206
+ graphs: graphs.map((g) => ({
207
+ ...g,
208
+ path: subsystemModelFilePath(g.id),
209
+ })),
210
+ }, null, 2) + '\n');
211
+ }
212
+ async function getAction(id) {
213
+ if (!id) {
214
+ process.stderr.write('Pass a model id.\n');
215
+ process.exit(2);
216
+ }
217
+ const graph = await getSubsystemModel(id);
218
+ if (!graph) {
219
+ process.stderr.write(`Model not found: ${id}\n`);
220
+ process.exit(2);
221
+ }
222
+ process.stdout.write(JSON.stringify({ ok: true, graph }, null, 2) + '\n');
223
+ }
224
+ export function createSubsystemModelCommand() {
225
+ const cmd = new Command('subsystem-model').description('Create, open, and list subsystem models (works without Studio already running)');
226
+ cmd
227
+ .command('create')
228
+ .description('Validate + persist a subsystem model JSON, then open it in Principal Studio')
229
+ .option('-f, --file <path>', 'Path to model JSON (default: stdin; use - for stdin)')
230
+ .option('--no-open', 'Persist only; do not open Principal Studio')
231
+ .option('--viewer-dir <path>', 'Path to the @principal-ai/subsystems-studio package (overrides PRINCIPAL_STUDIO_DIR)')
232
+ .action(createAction);
233
+ cmd
234
+ .command('open')
235
+ .description('Open a stored subsystem model in Principal Studio')
236
+ .argument('[id]', 'Model id (sg-…)')
237
+ .option('--viewer-dir <path>', 'Path to the @principal-ai/subsystems-studio package (overrides PRINCIPAL_STUDIO_DIR)')
238
+ .action(openAction);
239
+ cmd
240
+ .command('list')
241
+ .description('List stored subsystem models')
242
+ .action(listAction);
243
+ cmd
244
+ .command('get')
245
+ .description('Print a stored subsystem model as JSON')
246
+ .argument('[id]', 'Model id (sg-…)')
247
+ .action(getAction);
248
+ return cmd;
249
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Topic command — create / list / view / add-trail on web-ade topics.
3
+ *
4
+ * A **topic** is a curated collection of trails on a shared subject. The
5
+ * topic record itself is `{ title, description, trailIds, owner }` — trails
6
+ * are referenced by id and continue to enforce their own repo-access checks
7
+ * on read. See docs/topics.md in web-ade for the full design.
8
+ *
9
+ * Resolves a GitHub token locally (gh CLI → git credential helper) and calls
10
+ * the web-ade topic API with `Authorization: Bearer <token>`. The token is
11
+ * never echoed to argv, env, stdout, or stderr.
12
+ */
13
+ import { Command } from 'commander';
14
+ export declare function createTopicCommand(): Command;
15
+ //# sourceMappingURL=topic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topic.d.ts","sourceRoot":"","sources":["../../src/commands/topic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqsBpC,wBAAgB,kBAAkB,IAAI,OAAO,CAuH5C"}