@postplus/cli 0.1.31 → 0.1.33

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/README.md CHANGED
@@ -63,8 +63,9 @@ postplus studio status
63
63
  ```
64
64
 
65
65
  Studio creates a visible `PostPlus Studio/` folder in the current working
66
- directory. Assets, workflow files, activity, and provenance live inside that
67
- folder; hidden runtime cache and logs stay under `PostPlus Studio/.postplus/`.
66
+ directory and opens the bundled local dashboard from the public CLI package.
67
+ Assets, workflow files, activity, and provenance live inside that folder; hidden
68
+ runtime cache and logs stay under `PostPlus Studio/.postplus/`.
68
69
 
69
70
  ## The Vision
70
71
 
package/build/index.js CHANGED
@@ -43,7 +43,7 @@ Usage:
43
43
  postplus doctor [--skill <skill-id>] [--json]
44
44
  postplus quote confirm --json --challenge-file <path>
45
45
  postplus skills verify [--json]
46
- postplus studio init|open|status
46
+ postplus studio init|open|status Open bundled Local Studio
47
47
  postplus update [--current-directory]
48
48
  postplus uninstall [--current-directory]
49
49
  postplus list [--json]
@@ -2,6 +2,7 @@ import { writeCurrentCliVersionToLocalConfig } from './client-compatibility.js';
2
2
  import { runCommand, runInteractiveCommand } from './command-runner.js';
3
3
  import { clearManagedSkillBaseline, readManagedSkillBaseline, writeManagedSkillBaseline, } from './local-state.js';
4
4
  import { POSTPLUS_SKILLS_AGENT_TARGETS, formatPostPlusSkillsInstallCommand, resolvePostPlusSkillsSource, loadPublicSkillCatalog, } from './skill-catalog.js';
5
+ import { clearUpdateCheckCache } from './update-check.js';
5
6
  const NPX_SKILLS = ['-y', 'skills'];
6
7
  const DEFAULT_SKILL_MUTATION_OPTIONS = {
7
8
  scope: 'global',
@@ -31,6 +32,7 @@ export async function runPostPlusSkillUpdate(dependencies = {
31
32
  skillNames,
32
33
  });
33
34
  await writeCurrentCliVersionToLocalConfig();
35
+ await clearUpdateCheckCache();
34
36
  return 0;
35
37
  }
36
38
  export async function runPostPlusSkillUninstall(dependencies = {
@@ -46,6 +48,7 @@ export async function runPostPlusSkillUninstall(dependencies = {
46
48
  const exitCode = await dependencies.runInteractiveCommand('npx', buildPostPlusSkillUninstallArgs(allKnownSkillNames, options.scope));
47
49
  if (exitCode === 0) {
48
50
  await clearManagedSkillBaseline();
51
+ await clearUpdateCheckCache();
49
52
  }
50
53
  return exitCode;
51
54
  }
@@ -72,6 +75,7 @@ export async function runPostPlusSkillVerify(dependencies = {
72
75
  skillNames: inspection.requiredSkillNames,
73
76
  });
74
77
  await writeCurrentCliVersionToLocalConfig();
78
+ await clearUpdateCheckCache();
75
79
  return {
76
80
  ...inspection.report,
77
81
  baselineUpdated: true,
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ import http from 'node:http';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { existsSync } from 'node:fs';
5
+ import { basename, join, resolve } from 'node:path';
6
+ export async function startStudioServer(argv = process.argv.slice(2)) {
7
+ const options = parseOptions(argv);
8
+ const server = http.createServer(async (request, response) => {
9
+ try {
10
+ await handleRequest(request, response, options);
11
+ }
12
+ catch (error) {
13
+ sendJson(response, 500, {
14
+ error: error instanceof Error ? error.message : String(error),
15
+ ok: false,
16
+ });
17
+ }
18
+ });
19
+ await new Promise((resolveListen, rejectListen) => {
20
+ server.once('error', rejectListen);
21
+ server.listen(options.port, options.host, () => {
22
+ server.off('error', rejectListen);
23
+ resolveListen();
24
+ });
25
+ });
26
+ process.on('SIGTERM', () => {
27
+ server.close(() => process.exit(0));
28
+ });
29
+ process.on('SIGINT', () => {
30
+ server.close(() => process.exit(0));
31
+ });
32
+ return server;
33
+ }
34
+ function parseOptions(argv) {
35
+ const options = {};
36
+ for (let index = 0; index < argv.length; index += 1) {
37
+ const arg = argv[index];
38
+ if (arg === '--studio-root') {
39
+ const value = readOptionValue(argv, index, arg);
40
+ options.studioRoot = resolve(value);
41
+ index += 1;
42
+ continue;
43
+ }
44
+ if (arg === '--host') {
45
+ options.host = readOptionValue(argv, index, arg);
46
+ index += 1;
47
+ continue;
48
+ }
49
+ if (arg === '--port') {
50
+ const value = Number(readOptionValue(argv, index, arg));
51
+ if (!Number.isInteger(value) || value <= 0) {
52
+ throw new Error('--port must be a positive integer.');
53
+ }
54
+ options.port = value;
55
+ index += 1;
56
+ continue;
57
+ }
58
+ if (arg === '--help' || arg === '-h') {
59
+ process.stdout.write(`Usage:
60
+ node build/studio-server.js --studio-root <dir> --host 127.0.0.1 --port 3978
61
+ `);
62
+ process.exit(0);
63
+ }
64
+ throw new Error(`Unknown Studio server option: ${arg}`);
65
+ }
66
+ if (!options.studioRoot) {
67
+ throw new Error('Studio server requires --studio-root.');
68
+ }
69
+ return {
70
+ host: options.host ?? '127.0.0.1',
71
+ port: options.port ?? 3978,
72
+ studioRoot: options.studioRoot,
73
+ };
74
+ }
75
+ function readOptionValue(argv, index, name) {
76
+ const value = argv[index + 1];
77
+ if (!value || value.startsWith('--')) {
78
+ throw new Error(`Missing value for ${name}.`);
79
+ }
80
+ return value;
81
+ }
82
+ async function handleRequest(request, response, options) {
83
+ const url = new URL(request.url ?? '/', `http://${options.host}`);
84
+ if (request.method !== 'GET') {
85
+ sendJson(response, 405, { error: 'Method not allowed.', ok: false });
86
+ return;
87
+ }
88
+ if (url.pathname === '/api/health') {
89
+ sendJson(response, 200, { ok: true });
90
+ return;
91
+ }
92
+ if (url.pathname === '/api/project') {
93
+ sendJson(response, 200, await readStudioSnapshot(options.studioRoot));
94
+ return;
95
+ }
96
+ if (url.pathname === '/' ||
97
+ url.pathname === '/dashboard' ||
98
+ url.pathname === '/dashboard/') {
99
+ sendHtml(response, renderDashboardHtml());
100
+ return;
101
+ }
102
+ sendJson(response, 404, { error: 'Not found.', ok: false });
103
+ }
104
+ async function readStudioSnapshot(studioRoot) {
105
+ return {
106
+ activity: await readJsonLines(join(studioRoot, 'activity.jsonl')),
107
+ manifest: await readJsonFile(join(studioRoot, 'manifest.json')),
108
+ pipeline: await readJsonFile(join(studioRoot, 'pipeline.json')),
109
+ project: await readJsonFile(join(studioRoot, 'project.json')),
110
+ provenance: await readJsonLines(join(studioRoot, 'provenance.jsonl')),
111
+ studio: await readJsonFile(join(studioRoot, 'studio.json')),
112
+ studioRoot,
113
+ };
114
+ }
115
+ async function readJsonFile(path) {
116
+ if (!existsSync(path)) {
117
+ return null;
118
+ }
119
+ return JSON.parse(await readFile(path, 'utf8'));
120
+ }
121
+ async function readJsonLines(path) {
122
+ if (!existsSync(path)) {
123
+ return [];
124
+ }
125
+ const lines = (await readFile(path, 'utf8'))
126
+ .split('\n')
127
+ .map((line) => line.trim())
128
+ .filter(Boolean);
129
+ return lines.slice(-50).map((line) => JSON.parse(line));
130
+ }
131
+ function sendJson(response, statusCode, payload) {
132
+ response.writeHead(statusCode, {
133
+ 'cache-control': 'no-store',
134
+ 'content-type': 'application/json; charset=utf-8',
135
+ });
136
+ response.end(`${JSON.stringify(payload, null, 2)}\n`);
137
+ }
138
+ function sendHtml(response, html) {
139
+ response.writeHead(200, {
140
+ 'cache-control': 'no-store',
141
+ 'content-type': 'text/html; charset=utf-8',
142
+ });
143
+ response.end(html);
144
+ }
145
+ function renderDashboardHtml() {
146
+ return `<!doctype html>
147
+ <html lang="en">
148
+ <head>
149
+ <meta charset="utf-8" />
150
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
151
+ <title>PostPlus Studio</title>
152
+ <style>
153
+ :root { color-scheme: light; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
154
+ body { margin: 0; background: #f7f7f4; color: #1f2933; }
155
+ header { border-bottom: 1px solid #d6d8dc; background: #ffffff; padding: 20px 28px; }
156
+ main { display: grid; gap: 16px; grid-template-columns: 280px minmax(0, 1fr); padding: 20px 28px 28px; }
157
+ h1 { font-size: 22px; line-height: 1.2; margin: 0 0 6px; }
158
+ h2 { font-size: 13px; letter-spacing: 0; line-height: 1.25; margin: 0 0 10px; text-transform: uppercase; color: #5b6472; }
159
+ p { margin: 0; }
160
+ .subtle { color: #627083; font-size: 13px; }
161
+ .panel { background: #ffffff; border: 1px solid #d8dce2; border-radius: 8px; padding: 14px; min-width: 0; }
162
+ .stack { display: grid; gap: 12px; }
163
+ .grid { display: grid; gap: 12px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
164
+ .row { align-items: center; display: flex; justify-content: space-between; gap: 12px; border-top: 1px solid #edf0f2; padding-top: 10px; margin-top: 10px; }
165
+ .label { color: #5b6472; font-size: 12px; }
166
+ .value { font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
167
+ .step { border: 1px solid #dfe3e8; border-radius: 6px; padding: 10px; background: #fbfbfa; }
168
+ .step-title { font-size: 13px; font-weight: 700; }
169
+ .status { color: #0f766e; font-size: 12px; margin-top: 4px; }
170
+ pre { background: #111827; border-radius: 8px; color: #e5e7eb; font-size: 12px; line-height: 1.45; margin: 0; max-height: 420px; overflow: auto; padding: 12px; white-space: pre-wrap; }
171
+ @media (max-width: 840px) { main { grid-template-columns: 1fr; padding: 16px; } header { padding: 18px 16px; } .grid { grid-template-columns: 1fr; } }
172
+ </style>
173
+ </head>
174
+ <body>
175
+ <header>
176
+ <h1>PostPlus Studio</h1>
177
+ <p class="subtle" id="studio-root">Loading workspace...</p>
178
+ </header>
179
+ <main>
180
+ <section class="stack">
181
+ <div class="panel">
182
+ <h2>Project</h2>
183
+ <div class="row"><span class="label">Name</span><span class="value" id="project-name">-</span></div>
184
+ <div class="row"><span class="label">Status</span><span class="value" id="project-status">-</span></div>
185
+ </div>
186
+ <div class="panel">
187
+ <h2>Pipeline</h2>
188
+ <div id="pipeline-steps" class="stack"></div>
189
+ </div>
190
+ </section>
191
+ <section class="stack">
192
+ <div class="grid">
193
+ <div class="panel"><h2>Assets</h2><p class="value" id="asset-count">-</p></div>
194
+ <div class="panel"><h2>Activity</h2><p class="value" id="activity-count">-</p></div>
195
+ <div class="panel"><h2>Provenance</h2><p class="value" id="provenance-count">-</p></div>
196
+ </div>
197
+ <div class="panel">
198
+ <h2>Workspace JSON</h2>
199
+ <pre id="snapshot">{}</pre>
200
+ </div>
201
+ </section>
202
+ </main>
203
+ <script>
204
+ const text = (id, value) => { document.getElementById(id).textContent = value ?? '-'; };
205
+ const render = async () => {
206
+ const response = await fetch('/api/project');
207
+ const data = await response.json();
208
+ const project = data.project || {};
209
+ const pipeline = data.pipeline || {};
210
+ const manifest = data.manifest || {};
211
+ text('studio-root', data.studioRoot || '');
212
+ text('project-name', project.name || project.project_id || 'PostPlus Studio');
213
+ text('project-status', project.status || 'active');
214
+ text('asset-count', Array.isArray(manifest.assets) ? String(manifest.assets.length) : '0');
215
+ text('activity-count', Array.isArray(data.activity) ? String(data.activity.length) : '0');
216
+ text('provenance-count', Array.isArray(data.provenance) ? String(data.provenance.length) : '0');
217
+ const steps = Array.isArray(pipeline.steps) ? pipeline.steps : [];
218
+ document.getElementById('pipeline-steps').innerHTML = steps.map((step) =>
219
+ '<div class="step"><div class="step-title">' + escapeHtml(step.name || step.id || 'Step') + '</div><div class="status">' + escapeHtml(step.status || 'pending') + '</div></div>'
220
+ ).join('') || '<p class="subtle">No pipeline steps yet.</p>';
221
+ document.getElementById('snapshot').textContent = JSON.stringify(data, null, 2);
222
+ };
223
+ const escapeHtml = (value) => String(value).replace(/[&<>"']/g, (char) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[char]));
224
+ render().catch((error) => {
225
+ document.getElementById('snapshot').textContent = error.stack || error.message || String(error);
226
+ });
227
+ </script>
228
+ </body>
229
+ </html>`;
230
+ }
231
+ if (process.argv[1] && basename(process.argv[1])?.startsWith('studio-server')) {
232
+ startStudioServer().catch((error) => {
233
+ process.stderr.write(`${error instanceof Error ? error.stack || error.message : String(error)}\n`);
234
+ process.exit(1);
235
+ });
236
+ }
package/build/studio.js CHANGED
@@ -1,6 +1,7 @@
1
- import { spawn, spawnSync } from 'node:child_process';
2
- import { access, mkdir, writeFile, } from 'node:fs/promises';
3
- import { constants as fsConstants } from 'node:fs';
1
+ import { spawn } from 'node:child_process';
2
+ import { access, mkdir, readFile, writeFile, } from 'node:fs/promises';
3
+ import { closeSync, constants as fsConstants, openSync } from 'node:fs';
4
+ import net from 'node:net';
4
5
  import { platform } from 'node:os';
5
6
  import { basename, dirname, join, resolve, } from 'node:path';
6
7
  import { fileURLToPath } from 'node:url';
@@ -12,6 +13,10 @@ export async function runStudioCommand(args) {
12
13
  printStudioHelp();
13
14
  return 0;
14
15
  }
16
+ if (rest.some((arg) => ['help', '--help', '-h'].includes(arg))) {
17
+ printStudioHelp();
18
+ return 0;
19
+ }
15
20
  const options = parseStudioOptions(rest);
16
21
  switch (subcommand) {
17
22
  case 'init': {
@@ -43,7 +48,8 @@ Usage:
43
48
  postplus studio open [--workdir <dir>] [--port 3978] [--no-browser] [--json]
44
49
  postplus studio status [--workdir <dir>] [--json]
45
50
 
46
- Studio creates a visible "PostPlus Studio" folder inside the selected working directory.
51
+ Local Studio is a public local workspace included in the PostPlus CLI package.
52
+ Studio creates a visible "PostPlus Studio" folder inside the selected working directory and opens the bundled local dashboard.
47
53
  `);
48
54
  }
49
55
  function parseStudioOptions(args) {
@@ -192,76 +198,155 @@ async function getStudioStatus(workdir) {
192
198
  }
193
199
  async function openStudio(options) {
194
200
  const { studioRoot } = await initializeStudio(options.workdir);
195
- const runtimeRoot = await resolveStudioRuntimeRoot();
196
- const launcher = join(runtimeRoot, 'skills/00-core/postplus-workspace-dashboard/scripts/launch_workspace_dashboard.mjs');
197
- const result = spawnSync(process.execPath, [
198
- launcher,
201
+ const parsed = await launchBundledStudioServer(studioRoot, options.port);
202
+ if (options.browser) {
203
+ openSystemBrowser(parsed.url);
204
+ }
205
+ return {
206
+ ok: true,
207
+ studioRoot,
208
+ ...parsed,
209
+ };
210
+ }
211
+ async function launchBundledStudioServer(studioRoot, startPort) {
212
+ const existing = await readLiveStudioServerState(studioRoot);
213
+ if (existing) {
214
+ return {
215
+ logPath: existing.logPath,
216
+ pid: existing.pid,
217
+ reused: true,
218
+ url: existing.dashboardUrl,
219
+ };
220
+ }
221
+ const host = '127.0.0.1';
222
+ const port = await findAvailablePort(startPort, host);
223
+ const baseUrl = `http://${host}:${port}`;
224
+ const dashboardUrl = `${baseUrl}/dashboard/`;
225
+ const logDir = join(studioRoot, '.postplus', 'logs');
226
+ await mkdir(logDir, { recursive: true });
227
+ const logPath = join(logDir, 'studio-server.log');
228
+ const logFd = openSync(logPath, 'a');
229
+ const serverEntrypoint = resolveBundledStudioServerEntrypoint();
230
+ const packageRoot = resolveCliPackageRoot();
231
+ const child = spawn(process.execPath, [
232
+ ...buildNodeLoaderArgs(serverEntrypoint),
233
+ serverEntrypoint,
199
234
  '--studio-root',
200
235
  studioRoot,
201
236
  '--host',
202
- '127.0.0.1',
237
+ host,
203
238
  '--port',
204
- String(options.port),
205
- '--skip-build',
239
+ String(port),
206
240
  ], {
207
- cwd: runtimeRoot,
208
- encoding: 'utf8',
241
+ cwd: packageRoot,
242
+ detached: true,
243
+ stdio: ['ignore', logFd, logFd],
209
244
  });
210
- if (result.status !== 0) {
211
- throw new Error(result.stderr || result.stdout || 'Failed to open Studio.');
245
+ child.unref();
246
+ closeSync(logFd);
247
+ try {
248
+ await waitForStudioServer(baseUrl, logPath);
212
249
  }
213
- const parsed = JSON.parse(result.stdout);
214
- if (options.browser) {
215
- openSystemBrowser(parsed.url);
250
+ catch (error) {
251
+ if (child.pid) {
252
+ try {
253
+ process.kill(child.pid);
254
+ }
255
+ catch {
256
+ // The process already exited; the readiness error below carries the failure.
257
+ }
258
+ }
259
+ throw error;
216
260
  }
217
- return {
218
- ok: true,
219
- runtimeRoot,
261
+ const state = {
262
+ baseUrl,
263
+ dashboardUrl,
264
+ logPath,
265
+ pid: child.pid,
266
+ startedAt: new Date().toISOString(),
220
267
  studioRoot,
221
- ...parsed,
222
268
  };
269
+ await writeJson(getStudioServerStatePath(studioRoot), state);
270
+ return {
271
+ logPath,
272
+ pid: child.pid,
273
+ reused: false,
274
+ url: dashboardUrl,
275
+ };
276
+ }
277
+ function resolveBundledStudioServerEntrypoint() {
278
+ const currentModulePath = fileURLToPath(import.meta.url);
279
+ const extension = currentModulePath.endsWith('.ts') ? '.ts' : '.js';
280
+ return join(dirname(currentModulePath), `studio-server${extension}`);
281
+ }
282
+ function resolveCliPackageRoot() {
283
+ return dirname(dirname(fileURLToPath(import.meta.url)));
284
+ }
285
+ function buildNodeLoaderArgs(entrypoint) {
286
+ return entrypoint.endsWith('.ts') ? ['--import', 'tsx'] : [];
223
287
  }
224
- async function resolveStudioRuntimeRoot() {
225
- const envRoot = process.env.POSTPLUS_STUDIO_RUNTIME_ROOT?.trim();
226
- if (envRoot) {
227
- return assertStudioRuntimeRoot(resolve(envRoot));
288
+ async function readLiveStudioServerState(studioRoot) {
289
+ const state = await readJsonIfExists(getStudioServerStatePath(studioRoot));
290
+ if (!state?.baseUrl || !state.dashboardUrl) {
291
+ return null;
228
292
  }
229
- const candidates = [
230
- process.cwd(),
231
- dirname(fileURLToPath(import.meta.url)),
232
- ...ancestorDirs(process.cwd()),
233
- ];
234
- for (const base of candidates) {
235
- for (const candidate of [
236
- base,
237
- join(base, 'packages/vibe_marketing'),
238
- join(base, '../packages/vibe_marketing'),
239
- join(base, '../../packages/vibe_marketing'),
240
- ]) {
241
- if (await isStudioRuntimeRoot(resolve(candidate))) {
242
- return resolve(candidate);
243
- }
293
+ if (await canFetchStudioServer(state.baseUrl)) {
294
+ return state;
295
+ }
296
+ return null;
297
+ }
298
+ function getStudioServerStatePath(studioRoot) {
299
+ return join(studioRoot, '.postplus', 'studio-server.json');
300
+ }
301
+ async function readJsonIfExists(path) {
302
+ if (!(await pathExists(path))) {
303
+ return null;
304
+ }
305
+ try {
306
+ return JSON.parse(await readFile(path, 'utf8'));
307
+ }
308
+ catch {
309
+ return null;
310
+ }
311
+ }
312
+ async function waitForStudioServer(baseUrl, logPath) {
313
+ const startedAt = Date.now();
314
+ while (Date.now() - startedAt < 5000) {
315
+ if (await canFetchStudioServer(baseUrl)) {
316
+ return;
244
317
  }
318
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, 150));
245
319
  }
246
- throw new Error('PostPlus Studio runtime was not found. Set POSTPLUS_STUDIO_RUNTIME_ROOT to the vibe_marketing authoring repo.');
320
+ throw new Error(`Studio server did not become ready at ${baseUrl}. See log: ${logPath}`);
247
321
  }
248
- function ancestorDirs(start) {
249
- const dirs = [];
250
- let current = resolve(start);
251
- while (dirname(current) !== current) {
252
- current = dirname(current);
253
- dirs.push(current);
322
+ async function canFetchStudioServer(baseUrl) {
323
+ try {
324
+ const response = await fetch(`${baseUrl.replace(/\/$/u, '')}/api/health`, {
325
+ signal: AbortSignal.timeout(1200),
326
+ });
327
+ return response.ok;
328
+ }
329
+ catch {
330
+ return false;
254
331
  }
255
- return dirs;
256
332
  }
257
- async function assertStudioRuntimeRoot(root) {
258
- if (!(await isStudioRuntimeRoot(root))) {
259
- throw new Error(`Invalid PostPlus Studio runtime root: ${root}`);
333
+ async function findAvailablePort(startPort, host) {
334
+ for (let port = startPort; port < startPort + 50; port += 1) {
335
+ if (await isPortAvailable(port, host)) {
336
+ return port;
337
+ }
260
338
  }
261
- return root;
339
+ throw new Error(`No available Studio port found from ${startPort} to ${startPort + 49}.`);
262
340
  }
263
- async function isStudioRuntimeRoot(root) {
264
- return pathExists(join(root, 'skills/00-core/postplus-workspace-dashboard/scripts/launch_workspace_dashboard.mjs'));
341
+ function isPortAvailable(port, host) {
342
+ return new Promise((resolveAvailable) => {
343
+ const server = net.createServer();
344
+ server.once('error', () => resolveAvailable(false));
345
+ server.once('listening', () => {
346
+ server.close(() => resolveAvailable(true));
347
+ });
348
+ server.listen(port, host);
349
+ });
265
350
  }
266
351
  async function pathExists(path) {
267
352
  try {
@@ -1,4 +1,4 @@
1
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
1
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { readCurrentCliVersion } from './client-compatibility.js';
4
4
  import { runInteractiveCommand as runDefaultInteractiveCommand, } from './command-runner.js';
@@ -88,6 +88,11 @@ export async function refreshUpdateCheckCache() {
88
88
  force: true,
89
89
  });
90
90
  }
91
+ export async function clearUpdateCheckCache() {
92
+ await rm(getUpdateCheckCachePath(), {
93
+ force: true,
94
+ });
95
+ }
91
96
  export async function runCliSelfUpdateIfOutdated(dependencies = {}) {
92
97
  const fetchFn = dependencies.fetchFn ?? fetch;
93
98
  const runInteractiveCommand = dependencies.runInteractiveCommand ?? runDefaultInteractiveCommand;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postplus/cli",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
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.",
@@ -23,6 +23,7 @@
23
23
  "build/skill-management.js",
24
24
  "build/status.js",
25
25
  "build/studio.js",
26
+ "build/studio-server.js",
26
27
  "build/subscription-status.js",
27
28
  "build/update-check.js",
28
29
  "LICENSE",