@postplus/cli 0.1.27 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/doctor.js CHANGED
@@ -2,6 +2,7 @@ import { resolveFreshRemoteAuth, } from './auth-session.js';
2
2
  import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, } from './client-compatibility.js';
3
3
  import { resolveHostedBaseUrl } from './hosted-release.js';
4
4
  import { formatLocalDependencyReport, generateLocalDependencyReport, } from './local-dependencies.js';
5
+ import { loadPublicSkillCatalog, } from './skill-catalog.js';
5
6
  import { readSubscriptionStatusField } from './subscription-status.js';
6
7
  function createPass(id, label, detail, severity = 'required') {
7
8
  return {
@@ -23,12 +24,16 @@ function createFail(id, label, detail, fix, input = {}) {
23
24
  metadata: input.metadata,
24
25
  };
25
26
  }
26
- export async function generateDoctorReport() {
27
+ export async function generateDoctorReport(options = {}) {
27
28
  const hostedBaseUrl = await resolveHostedBaseUrl();
28
29
  const checks = [
29
30
  createPass('hosted_base_url', 'PostPlus Cloud', `Using ${hostedBaseUrl ?? 'https://postplus.io'}`),
30
31
  ];
31
- checks.push(await checkLocalDependencies());
32
+ const skillScope = await resolveSkillScope(options.skillId);
33
+ if (skillScope) {
34
+ checks.push(createPass('skill_catalog', 'Skill selection', `Using ${skillScope.skill.skillId} from catalog ${skillScope.catalog.releaseId}`));
35
+ }
36
+ checks.push(await checkLocalDependencies(skillScope));
32
37
  if (!hostedBaseUrl) {
33
38
  checks.push(createFail('remote_auth', 'Remote auth', 'PostPlus Cloud base URL could not be resolved.', 'Configure POSTPLUS_API_BASE_URL or run `postplus auth login`.'));
34
39
  return buildDoctorReport(checks);
@@ -46,17 +51,40 @@ export async function generateDoctorReport() {
46
51
  const authCheck = await checkRemoteAuth(auth);
47
52
  checks.push(authCheck);
48
53
  if (authCheck.status === 'pass') {
49
- checks.push(await checkHostedCapabilities(auth));
54
+ checks.push(await checkHostedCapabilities(auth, skillScope));
55
+ }
56
+ return buildDoctorReport(checks, options.skillId);
57
+ }
58
+ async function resolveSkillScope(skillId) {
59
+ if (!skillId) {
60
+ return null;
61
+ }
62
+ const catalog = await loadPublicSkillCatalog();
63
+ const skill = catalog.skills.find((entry) => entry.skillId === skillId);
64
+ if (!skill) {
65
+ throw new Error(`Unknown PostPlus skill: ${skillId}. Run \`postplus list\` to see released skill ids.`);
50
66
  }
51
- return buildDoctorReport(checks);
67
+ return { catalog, skill };
52
68
  }
53
- async function checkLocalDependencies() {
69
+ async function checkLocalDependencies(skillScope) {
54
70
  try {
55
- const report = await generateLocalDependencyReport();
71
+ const report = await generateLocalDependencyReport(skillScope
72
+ ? {
73
+ loadCatalog: async () => ({
74
+ ...skillScope.catalog,
75
+ skills: [skillScope.skill],
76
+ }),
77
+ }
78
+ : {});
56
79
  const detail = formatLocalDependencyReport(report);
57
80
  if (!report.ok) {
58
- return createFail('local_dependencies', 'Task-specific local media dependencies', detail, 'Run the affected PostPlus skill in a local agent. The installed postplus-shared rules tell the agent how to bootstrap approved missing media dependencies.', {
59
- severity: 'task_specific',
81
+ const skillId = skillScope?.skill.skillId;
82
+ return createFail('local_dependencies', skillId
83
+ ? `Local dependencies for ${skillId}`
84
+ : 'Task-specific local media dependencies', detail, skillId
85
+ ? 'Run the selected PostPlus skill in a local agent. The installed postplus-shared rules tell the agent how to bootstrap approved missing dependencies.'
86
+ : 'Run the affected PostPlus skill in a local agent. The installed postplus-shared rules tell the agent how to bootstrap approved missing media dependencies.', {
87
+ severity: skillId ? 'required' : 'task_specific',
60
88
  metadata: {
61
89
  bootstrapRule: 'postplus-shared',
62
90
  missingDependencies: report.checks
@@ -69,7 +97,9 @@ async function checkLocalDependencies() {
69
97
  },
70
98
  });
71
99
  }
72
- return createPass('local_dependencies', 'Local dependencies', detail);
100
+ return createPass('local_dependencies', skillScope
101
+ ? `Local dependencies for ${skillScope.skill.skillId}`
102
+ : 'Local dependencies', detail);
73
103
  }
74
104
  catch (error) {
75
105
  return createFail('local_dependencies', 'Local dependencies', error instanceof Error
@@ -77,13 +107,14 @@ async function checkLocalDependencies() {
77
107
  : 'Failed to check local dependencies.');
78
108
  }
79
109
  }
80
- function buildDoctorReport(checks) {
110
+ function buildDoctorReport(checks, skillId) {
81
111
  const requiredOk = checks.every((check) => check.severity !== 'required' || check.status === 'pass');
82
112
  return {
83
113
  schemaVersion: 1,
84
114
  ok: checks.every((check) => check.status === 'pass'),
85
115
  requiredOk,
86
116
  checks,
117
+ ...(skillId ? { skillId } : {}),
87
118
  };
88
119
  }
89
120
  async function checkRemoteAuth(input) {
@@ -114,7 +145,7 @@ async function checkRemoteAuth(input) {
114
145
  : 'Failed to validate PostPlus Cloud auth.', 'Run `postplus auth validate` after confirming network access.');
115
146
  }
116
147
  }
117
- async function checkHostedCapabilities(input) {
148
+ async function checkHostedCapabilities(input, skillScope) {
118
149
  try {
119
150
  let response = await requestWithAuth(input, '/api/postplus-cli/hosted/readiness');
120
151
  if (response.status === 401) {
@@ -127,17 +158,28 @@ async function checkHostedCapabilities(input) {
127
158
  if (!response.ok) {
128
159
  return createFail('hosted_capabilities', 'Hosted capabilities', readErrorMessage(payload, 'PostPlus Cloud hosted readiness check failed.'));
129
160
  }
130
- const capabilities = Array.isArray(payload.capabilities)
131
- ? payload.capabilities
132
- : [];
133
- const failedLabels = capabilities
134
- .map(readCapabilityFailureLabel)
161
+ const capabilities = readHostedCapabilityEntries(payload.capabilities);
162
+ const relevantCapabilities = skillScope
163
+ ? filterCapabilitiesForSkill(capabilities, skillScope.skill.requirements)
164
+ : capabilities;
165
+ const failedLabels = relevantCapabilities
166
+ .map((value) => readCapabilityFailureLabel(value, skillScope))
135
167
  .filter((value) => value !== null);
136
- if (payload.ok !== true || failedLabels.length > 0) {
137
- return createFail('hosted_capabilities', 'Hosted capabilities', `Not ready: ${failedLabels.join(', ') || 'unknown capability failure'}`, 'Check PostPlus Cloud provider configuration and subscription state.');
168
+ if (skillScope && hasHostedRequirements(skillScope.skill.requirements)) {
169
+ const missingRequirements = collectMissingHostedRequirementLabels(relevantCapabilities, skillScope.skill.requirements);
170
+ failedLabels.push(...missingRequirements);
171
+ }
172
+ if (failedLabels.length > 0 ||
173
+ (!skillScope && payload.ok !== true && capabilities.length === 0)) {
174
+ const skillId = skillScope?.skill.skillId;
175
+ return createFail('hosted_capabilities', skillId ? `Hosted capabilities for ${skillId}` : 'Hosted capabilities', `Not ready: ${failedLabels.join(', ') || 'unknown capability failure'}`, 'Check PostPlus Cloud provider configuration and subscription state.', {
176
+ severity: skillId ? 'required' : 'task_specific',
177
+ });
138
178
  }
139
179
  const subscription = readSubscriptionStatusField(payload).label;
140
- return createPass('hosted_capabilities', 'Hosted capabilities', `Ready (${capabilities.length} capability checks passed; subscription ${subscription})`);
180
+ return createPass('hosted_capabilities', skillScope
181
+ ? `Hosted capabilities for ${skillScope.skill.skillId}`
182
+ : 'Hosted capabilities', `Ready (${relevantCapabilities.length} capability checks passed; subscription ${subscription})`);
141
183
  }
142
184
  catch (error) {
143
185
  return createFail('hosted_capabilities', 'Hosted capabilities', error instanceof Error
@@ -145,7 +187,13 @@ async function checkHostedCapabilities(input) {
145
187
  : 'Failed to check hosted capability readiness.');
146
188
  }
147
189
  }
148
- function readCapabilityFailureLabel(value) {
190
+ function readHostedCapabilityEntries(value) {
191
+ if (!Array.isArray(value)) {
192
+ return [];
193
+ }
194
+ return value.filter((entry) => !!entry && typeof entry === 'object' && !Array.isArray(entry));
195
+ }
196
+ function readCapabilityFailureLabel(value, skillScope) {
149
197
  if (!value || typeof value !== 'object') {
150
198
  return 'invalid capability response';
151
199
  }
@@ -163,9 +211,12 @@ function readCapabilityFailureLabel(value) {
163
211
  .map(readReadinessCheckFailureLabel)
164
212
  .filter((check) => check !== null)
165
213
  : [];
166
- return failedChecks.length > 0
214
+ const labelWithFailures = failedChecks.length > 0
167
215
  ? `${label} (${failedChecks.join(', ')})`
168
216
  : label;
217
+ return skillScope
218
+ ? `${labelWithFailures} for ${skillScope.skill.skillId}`
219
+ : labelWithFailures;
169
220
  }
170
221
  function readReadinessCheckFailureLabel(value) {
171
222
  if (!value || typeof value !== 'object') {
@@ -181,6 +232,128 @@ function readReadinessCheckFailureLabel(value) {
181
232
  ? record.id
182
233
  : 'unknown check';
183
234
  }
235
+ function filterCapabilitiesForSkill(capabilities, requirements) {
236
+ if (!hasHostedRequirements(requirements)) {
237
+ return [];
238
+ }
239
+ return capabilities.filter((capability) => capabilityMatchesRequirements(capability, requirements));
240
+ }
241
+ function capabilityMatchesRequirements(capability, requirements) {
242
+ const identifiers = collectCapabilityIdentifiers(capability);
243
+ const hostedCapabilities = new Set(requirements.hostedCapabilities);
244
+ const requirementKeys = collectHostedRequirementKeys(requirements);
245
+ return identifiers.some((identifier) => {
246
+ if (identifier === 'media-file:upload' &&
247
+ hostedCapabilities.has('media-file') &&
248
+ !requiresHostedMediaFileUpload(requirements)) {
249
+ return false;
250
+ }
251
+ const [prefix, suffix] = splitCapabilityIdentifier(identifier);
252
+ if (prefix === 'media-file' &&
253
+ suffix &&
254
+ suffix !== 'upload' &&
255
+ hostedCapabilities.has('media-file')) {
256
+ return true;
257
+ }
258
+ if (hostedCapabilities.has(identifier)) {
259
+ return true;
260
+ }
261
+ if (prefix &&
262
+ suffix &&
263
+ hostedCapabilities.has(prefix) &&
264
+ requirementKeys.has(suffix)) {
265
+ return true;
266
+ }
267
+ return requirementKeys.has(identifier) || requirementKeys.has(suffix);
268
+ });
269
+ }
270
+ function requiresHostedMediaFileUpload(requirements) {
271
+ return (requirements.hostedCapabilities.includes('media-generation') ||
272
+ requirements.endpointKeys.length > 0);
273
+ }
274
+ function collectCapabilityIdentifiers(capability) {
275
+ const identifiers = new Set();
276
+ for (const key of [
277
+ 'id',
278
+ 'key',
279
+ 'capability',
280
+ 'capabilityKey',
281
+ 'collectionKey',
282
+ 'endpointKey',
283
+ 'modelKey',
284
+ 'sourceKey',
285
+ 'accountConnection',
286
+ ]) {
287
+ const value = capability[key];
288
+ if (typeof value === 'string' && value.trim()) {
289
+ identifiers.add(value.trim());
290
+ }
291
+ }
292
+ if (Array.isArray(capability.checks)) {
293
+ for (const check of capability.checks) {
294
+ if (!check || typeof check !== 'object' || Array.isArray(check)) {
295
+ continue;
296
+ }
297
+ for (const identifier of collectCapabilityIdentifiers(check)) {
298
+ identifiers.add(identifier);
299
+ }
300
+ }
301
+ }
302
+ return [...identifiers];
303
+ }
304
+ function collectHostedRequirementKeys(requirements) {
305
+ return new Set([
306
+ ...requirements.accountConnections,
307
+ ...requirements.collectionKeys,
308
+ ...requirements.endpointKeys,
309
+ ...requirements.modelKeys,
310
+ ...requirements.sourceKeys,
311
+ ]);
312
+ }
313
+ function hasHostedRequirements(requirements) {
314
+ return (requirements.accountConnections.length > 0 ||
315
+ requirements.collectionKeys.length > 0 ||
316
+ requirements.endpointKeys.length > 0 ||
317
+ requirements.hostedCapabilities.length > 0 ||
318
+ requirements.modelKeys.length > 0 ||
319
+ requirements.sourceKeys.length > 0);
320
+ }
321
+ function collectMissingHostedRequirementLabels(capabilities, requirements) {
322
+ const availableIdentifiers = new Set(capabilities.flatMap(collectCapabilityIdentifiers));
323
+ const missing = [];
324
+ for (const capability of requirements.hostedCapabilities) {
325
+ if (![...availableIdentifiers].some((identifier) => identifierMatchesCapability(identifier, capability))) {
326
+ missing.push(capability);
327
+ }
328
+ }
329
+ for (const key of collectHostedRequirementKeys(requirements)) {
330
+ if (![...availableIdentifiers].some((identifier) => identifierMatchesKey(identifier, key))) {
331
+ missing.push(key);
332
+ }
333
+ }
334
+ return missing.map((value) => `${value} readiness check missing`);
335
+ }
336
+ function identifierMatchesKey(identifier, key) {
337
+ if (identifier === key) {
338
+ return true;
339
+ }
340
+ const [, suffix] = splitCapabilityIdentifier(identifier);
341
+ return suffix === key;
342
+ }
343
+ function identifierMatchesCapability(identifier, capability) {
344
+ if (identifier === capability) {
345
+ return true;
346
+ }
347
+ const [prefix] = splitCapabilityIdentifier(identifier);
348
+ return prefix === capability;
349
+ }
350
+ function splitCapabilityIdentifier(identifier) {
351
+ const index = identifier.indexOf(':');
352
+ if (index === -1) {
353
+ return [null, identifier];
354
+ }
355
+ return [identifier.slice(0, index), identifier.slice(index + 1)];
356
+ }
184
357
  function readErrorMessage(payload, fallback) {
185
358
  const compatibilityError = formatPostPlusCompatibilityError(payload);
186
359
  if (compatibilityError) {
package/build/index.js CHANGED
@@ -37,11 +37,11 @@ Usage:
37
37
  postplus auth status [--json]
38
38
  postplus auth validate [--json]
39
39
  postplus auth logout [--json]
40
- postplus doctor [--json]
40
+ postplus doctor [--skill <skill-id>] [--json]
41
41
  postplus update
42
42
  postplus uninstall
43
43
  postplus list [--json]
44
- postplus status [--json]
44
+ postplus status [--skill <skill-id>] [--json]
45
45
  postplus version
46
46
  postplus help
47
47
 
@@ -49,15 +49,15 @@ Skills:
49
49
  ${POSTPLUS_SKILLS_INSTALL_COMMAND}
50
50
  `);
51
51
  }
52
- async function runDoctor(json) {
53
- const report = await generateDoctorReport();
54
- if (json) {
52
+ async function runDoctor(options) {
53
+ const report = await generateDoctorReport({ skillId: options.skillId });
54
+ if (options.json) {
55
55
  writeJson(report);
56
56
  }
57
57
  else {
58
58
  process.stdout.write(`${formatDoctorReport(report)}\n`);
59
59
  }
60
- return report.ok ? 0 : 1;
60
+ return report.requiredOk ? 0 : 1;
61
61
  }
62
62
  async function runAuthStatus(json) {
63
63
  const report = await generateAuthStatusReport();
@@ -69,9 +69,9 @@ async function runAuthStatus(json) {
69
69
  }
70
70
  return report.ok ? 0 : 1;
71
71
  }
72
- async function runStatus(json) {
73
- const report = await generateStatusReport();
74
- if (json) {
72
+ async function runStatus(options) {
73
+ const report = await generateStatusReport({ skillId: options.skillId });
74
+ if (options.json) {
75
75
  writeJson(report);
76
76
  }
77
77
  else {
@@ -119,6 +119,29 @@ async function runSkillUninstallCommand() {
119
119
  function writeJson(value) {
120
120
  process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
121
121
  }
122
+ function parseDiagnosticOptions(args) {
123
+ const options = {
124
+ json: false,
125
+ };
126
+ for (let index = 0; index < args.length; index += 1) {
127
+ const arg = args[index];
128
+ if (arg === '--json') {
129
+ options.json = true;
130
+ continue;
131
+ }
132
+ if (arg === '--skill') {
133
+ const skillId = args[index + 1];
134
+ if (!skillId || skillId.startsWith('--')) {
135
+ throw new Error('Missing value for --skill.');
136
+ }
137
+ options.skillId = skillId;
138
+ index += 1;
139
+ continue;
140
+ }
141
+ throw new Error(`Unknown option for diagnostics command: ${arg}`);
142
+ }
143
+ return options;
144
+ }
122
145
  async function runAuthLogout(json) {
123
146
  const report = await clearAuthState();
124
147
  if (json) {
@@ -199,7 +222,7 @@ async function main() {
199
222
  return;
200
223
  }
201
224
  case 'doctor':
202
- process.exitCode = await runDoctor(json);
225
+ process.exitCode = await runDoctor(parseDiagnosticOptions(rest));
203
226
  return;
204
227
  case 'install':
205
228
  process.stderr.write(`PostPlus CLI does not install skills directly. Run \`${POSTPLUS_SKILLS_INSTALL_COMMAND}\`.\n`);
@@ -215,7 +238,7 @@ async function main() {
215
238
  process.exitCode = await runList(json);
216
239
  return;
217
240
  case 'status':
218
- process.exitCode = await runStatus(json);
241
+ process.exitCode = await runStatus(parseDiagnosticOptions(rest));
219
242
  return;
220
243
  case 'auth': {
221
244
  const [subcommand, ...authRest] = rest;
@@ -15,6 +15,15 @@ export const POSTPLUS_SKILLS_INSTALL_COMMAND = formatPostPlusSkillsInstallComman
15
15
  export const POSTPLUS_SKILLS_LIST_COMMAND = formatPostPlusSkillsListCommand();
16
16
  const POSTPLUS_SKILLS_INDEX_URL = 'https://raw.githubusercontent.com/PostPlusAI/postplus-skills/main/skills/INDEX.md';
17
17
  const POSTPLUS_SKILLS_CATALOG_URL = 'https://raw.githubusercontent.com/PostPlusAI/postplus-skills/main/skills/catalog.json';
18
+ export const PUBLIC_SKILL_REQUIREMENT_KEYS = [
19
+ 'accountConnections',
20
+ 'collectionKeys',
21
+ 'endpointKeys',
22
+ 'hostedCapabilities',
23
+ 'localDependencies',
24
+ 'modelKeys',
25
+ 'sourceKeys',
26
+ ];
18
27
  export async function loadPublicSkillCatalog(fetchFn = fetch, env = process.env) {
19
28
  const catalogUrl = resolvePostPlusSkillsCatalogUrl(env);
20
29
  const skillsSource = resolvePostPlusSkillsSource(env);
@@ -95,17 +104,7 @@ function parsePublicSkillCatalog(payload) {
95
104
  const path = typeof skill.path === 'string' && skill.path.trim()
96
105
  ? skill.path.trim()
97
106
  : null;
98
- const requirements = skill.requirements &&
99
- typeof skill.requirements === 'object' &&
100
- !Array.isArray(skill.requirements)
101
- ? skill.requirements
102
- : {};
103
- const localDependencies = Array.isArray(requirements.localDependencies)
104
- ? requirements.localDependencies
105
- .filter((value) => typeof value === 'string')
106
- .map((value) => value.trim())
107
- .filter(Boolean)
108
- : [];
107
+ const requirements = parsePublicSkillRequirements(skill.requirements);
109
108
  const status = typeof skill.status === 'string' ? skill.status.trim() : '';
110
109
  if (!skillId ||
111
110
  !path ||
@@ -113,9 +112,10 @@ function parsePublicSkillCatalog(payload) {
113
112
  throw new Error('PostPlus public skill catalog has an invalid skill.');
114
113
  }
115
114
  return {
116
- localDependencies,
115
+ localDependencies: requirements.localDependencies,
117
116
  skillId,
118
117
  path,
118
+ requirements,
119
119
  };
120
120
  });
121
121
  if (skills.length === 0) {
@@ -127,3 +127,40 @@ function parsePublicSkillCatalog(payload) {
127
127
  source,
128
128
  };
129
129
  }
130
+ function parsePublicSkillRequirements(value) {
131
+ if (value === undefined) {
132
+ return createEmptyRequirements();
133
+ }
134
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
135
+ throw new Error('PostPlus public skill catalog has invalid skill requirements.');
136
+ }
137
+ const record = value;
138
+ const requirements = createEmptyRequirements();
139
+ for (const key of PUBLIC_SKILL_REQUIREMENT_KEYS) {
140
+ const raw = record[key];
141
+ if (raw === undefined) {
142
+ continue;
143
+ }
144
+ if (!Array.isArray(raw)) {
145
+ throw new Error(`PostPlus public skill catalog has invalid ${key} requirements.`);
146
+ }
147
+ requirements[key] = raw.map((item) => {
148
+ if (typeof item !== 'string' || !item.trim()) {
149
+ throw new Error(`PostPlus public skill catalog has invalid ${key} requirements.`);
150
+ }
151
+ return item.trim();
152
+ });
153
+ }
154
+ return requirements;
155
+ }
156
+ function createEmptyRequirements() {
157
+ return {
158
+ accountConnections: [],
159
+ collectionKeys: [],
160
+ endpointKeys: [],
161
+ hostedCapabilities: [],
162
+ localDependencies: [],
163
+ modelKeys: [],
164
+ sourceKeys: [],
165
+ };
166
+ }
package/build/status.js CHANGED
@@ -3,17 +3,17 @@ import { writeCurrentCliVersionToLocalConfig } from './client-compatibility.js';
3
3
  import { formatDoctorReport, generateDoctorReport, } from './doctor.js';
4
4
  import { formatSkillInstallStatusReport, generateSkillInstallStatusReport, } from './skill-management.js';
5
5
  import { formatUpdateStatusReport, generateUpdateStatusReport, } from './update-check.js';
6
- export async function generateStatusReport() {
7
- return generateStatusReportWithDependencies();
6
+ export async function generateStatusReport(options = {}) {
7
+ return generateStatusReportWithDependencies({}, options);
8
8
  }
9
- export async function generateStatusReportWithDependencies(dependencies = {}) {
9
+ export async function generateStatusReportWithDependencies(dependencies = {}, options = {}) {
10
10
  await writeCurrentCliVersionToLocalConfig();
11
11
  const generateAuthStatus = dependencies.generateAuthStatus ?? generateAuthStatusReport;
12
12
  const generateDoctor = dependencies.generateDoctor ?? generateDoctorReport;
13
13
  const generateSkillStatus = dependencies.generateSkillStatus ?? generateSkillInstallStatusReport;
14
14
  const generateUpdateStatus = dependencies.generateUpdateStatus ?? generateUpdateStatusReport;
15
15
  const [doctor, auth, skills, updates] = await Promise.all([
16
- generateDoctor(),
16
+ generateDoctor({ skillId: options.skillId }),
17
17
  generateAuthStatus(),
18
18
  generateSkillStatus(),
19
19
  generateUpdateStatus(),
@@ -25,6 +25,7 @@ export async function generateStatusReportWithDependencies(dependencies = {}) {
25
25
  auth,
26
26
  skills,
27
27
  updates,
28
+ ...(options.skillId ? { skillId: options.skillId } : {}),
28
29
  };
29
30
  }
30
31
  export function formatStatusReport(report) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postplus/cli",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017",
5
5
  "type": "module",
6
6
  "description": "PostPlus CLI for PostPlus Cloud auth, status, and diagnostics.",