@postplus/cli 0.1.22 → 0.1.24
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 +1 -1
- package/build/auth-lifecycle.js +4 -4
- package/build/auth-login.js +22 -5
- package/build/auth-session.js +7 -6
- package/build/auth-validate.js +4 -4
- package/build/client-compatibility.js +37 -6
- package/build/doctor.js +4 -3
- package/build/index.js +6 -2
- package/build/local-dependencies.js +3 -3
- package/build/local-state.js +4 -4
- package/build/skill-catalog.js +29 -12
- package/build/skill-management.js +26 -11
- package/build/update-check.js +67 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ PostPlus has three public surfaces that work together:
|
|
|
31
31
|
Requires Node.js and npm.
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
npm install -g @postplus/cli
|
|
34
|
+
npm install -g @postplus/cli@latest
|
|
35
35
|
postplus auth login
|
|
36
36
|
npx -y skills add PostPlusAI/postplus-skills --global --full-depth --skill '*' --agent claude-code codex cursor github-copilot windsurf trae trae-cn --yes
|
|
37
37
|
```
|
package/build/auth-lifecycle.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { refreshRemoteAuthSession } from './auth-session.js';
|
|
2
2
|
import { clearAuthState, generateAuthStatusReport } from './auth.js';
|
|
3
|
-
import { buildPostPlusClientCompatibilityHeaders,
|
|
3
|
+
import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, } from './client-compatibility.js';
|
|
4
4
|
import { requireHostedBaseUrl } from './hosted-release.js';
|
|
5
5
|
import { resolveCliSessionTokenState } from './local-state.js';
|
|
6
6
|
export async function refreshRemoteAuth() {
|
|
@@ -47,9 +47,9 @@ export async function revokeRemoteAuth() {
|
|
|
47
47
|
});
|
|
48
48
|
const payload = (await response.json());
|
|
49
49
|
if (!response.ok) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
throw new Error(
|
|
50
|
+
const compatibilityError = formatPostPlusCompatibilityError(payload);
|
|
51
|
+
if (compatibilityError) {
|
|
52
|
+
throw new Error(compatibilityError);
|
|
53
53
|
}
|
|
54
54
|
throw new Error('error' in payload && typeof payload.error === 'string'
|
|
55
55
|
? payload.error
|
package/build/auth-login.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, writeCurrentCliVersionToLocalConfig, } from './client-compatibility.js';
|
|
2
3
|
import { requireHostedBaseUrl } from './hosted-release.js';
|
|
3
4
|
import { setLocalSession } from './local-state.js';
|
|
4
5
|
export const CLI_AUTH_LOGIN_TIMEOUT_MS = 30 * 60 * 1000;
|
|
@@ -16,6 +17,10 @@ export async function loginWithCloudHandoff() {
|
|
|
16
17
|
'Waiting for browser sign-in...',
|
|
17
18
|
'',
|
|
18
19
|
].join('\n'));
|
|
20
|
+
const didOpen = openCloudAuthVerificationUrlIfConfigured(started.verificationUrl);
|
|
21
|
+
if (didOpen) {
|
|
22
|
+
process.stdout.write('Browser opened for sign-in.\n\n');
|
|
23
|
+
}
|
|
19
24
|
const handoffPayload = await waitForCloudAuthLogin({
|
|
20
25
|
apiBaseUrl: baseUrl,
|
|
21
26
|
expiresAt: started.expiresAt,
|
|
@@ -63,6 +68,16 @@ export async function startCloudAuthLogin(apiBaseUrl) {
|
|
|
63
68
|
}
|
|
64
69
|
return payload;
|
|
65
70
|
}
|
|
71
|
+
export function openCloudAuthVerificationUrlIfConfigured(verificationUrl) {
|
|
72
|
+
const command = process.env.POSTPLUS_CLI_AUTH_OPEN_URL_COMMAND?.trim();
|
|
73
|
+
if (!command) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
execFileSync(command, [verificationUrl], {
|
|
77
|
+
stdio: 'ignore',
|
|
78
|
+
});
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
66
81
|
async function waitForCloudAuthLogin(input) {
|
|
67
82
|
const expiresAtMs = Date.parse(input.expiresAt);
|
|
68
83
|
const deadlineMs = Number.isFinite(expiresAtMs)
|
|
@@ -130,8 +145,9 @@ export function formatCliSessionAuthError(payload) {
|
|
|
130
145
|
'Once the environment is ready, the CLI will automatically obtain and store its session.',
|
|
131
146
|
].join(' ');
|
|
132
147
|
}
|
|
133
|
-
|
|
134
|
-
|
|
148
|
+
const compatibilityError = formatPostPlusCompatibilityError(payload);
|
|
149
|
+
if (compatibilityError) {
|
|
150
|
+
return compatibilityError;
|
|
135
151
|
}
|
|
136
152
|
if (typeof payload.error === 'string' && payload.error.trim().length > 0) {
|
|
137
153
|
return payload.error;
|
|
@@ -158,8 +174,9 @@ function isCliAuthLoginPendingPayload(payload) {
|
|
|
158
174
|
return 'status' in payload && payload.status === 'pending';
|
|
159
175
|
}
|
|
160
176
|
function formatRemoteAuthLoginError(payload) {
|
|
161
|
-
|
|
162
|
-
|
|
177
|
+
const compatibilityError = formatPostPlusCompatibilityError(payload);
|
|
178
|
+
if (compatibilityError) {
|
|
179
|
+
return compatibilityError;
|
|
163
180
|
}
|
|
164
181
|
return 'error' in payload &&
|
|
165
182
|
typeof payload.error === 'string' &&
|
package/build/auth-session.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { buildPostPlusClientCompatibilityHeaders,
|
|
1
|
+
import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, writeCurrentCliVersionToLocalConfig, } from './client-compatibility.js';
|
|
2
2
|
import { requireHostedBaseUrl } from './hosted-release.js';
|
|
3
|
-
import { resolveCliSessionTokenState, setLocalSession
|
|
3
|
+
import { resolveCliSessionTokenState, setLocalSession } from './local-state.js';
|
|
4
4
|
export async function resolveFreshRemoteAuth(options = {}) {
|
|
5
5
|
const [apiBaseUrl, cliSessionTokenState] = await Promise.all([
|
|
6
6
|
requireHostedBaseUrl(),
|
|
@@ -53,9 +53,9 @@ export async function refreshRemoteAuthSession(input) {
|
|
|
53
53
|
});
|
|
54
54
|
const payload = (await response.json());
|
|
55
55
|
if (!response.ok) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
throw new Error(
|
|
56
|
+
const compatibilityError = formatPostPlusCompatibilityError(payload);
|
|
57
|
+
if (compatibilityError) {
|
|
58
|
+
throw new Error(compatibilityError);
|
|
59
59
|
}
|
|
60
60
|
throw new Error('error' in payload && typeof payload.error === 'string'
|
|
61
61
|
? payload.error
|
|
@@ -83,7 +83,8 @@ function isRemoteAuthRefreshSuccessPayload(payload) {
|
|
|
83
83
|
payload !== null &&
|
|
84
84
|
typeof payload.cliSessionToken ===
|
|
85
85
|
'string' &&
|
|
86
|
-
payload.cliSessionToken.trim().length >
|
|
86
|
+
payload.cliSessionToken.trim().length >
|
|
87
|
+
0 &&
|
|
87
88
|
typeof payload.accountId === 'string' &&
|
|
88
89
|
typeof payload.userId === 'string');
|
|
89
90
|
}
|
package/build/auth-validate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveFreshRemoteAuth } from './auth-session.js';
|
|
2
|
-
import { buildPostPlusClientCompatibilityHeaders,
|
|
2
|
+
import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, } from './client-compatibility.js';
|
|
3
3
|
export async function validateRemoteAuth() {
|
|
4
4
|
let auth = await resolveFreshRemoteAuth();
|
|
5
5
|
let response = await fetchWhoami(auth);
|
|
@@ -11,9 +11,9 @@ export async function validateRemoteAuth() {
|
|
|
11
11
|
}
|
|
12
12
|
const payload = (await response.json());
|
|
13
13
|
if (!response.ok) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new Error(
|
|
14
|
+
const compatibilityError = formatPostPlusCompatibilityError(payload);
|
|
15
|
+
if (compatibilityError) {
|
|
16
|
+
throw new Error(compatibilityError);
|
|
17
17
|
}
|
|
18
18
|
throw new Error('error' in payload && typeof payload.error === 'string'
|
|
19
19
|
? payload.error
|
|
@@ -6,7 +6,7 @@ export const POSTPLUS_CLIENT_COMPATIBILITY_HEADERS = {
|
|
|
6
6
|
cliVersion: 'x-postplus-cli-version',
|
|
7
7
|
contractVersion: 'x-postplus-client-contract-version',
|
|
8
8
|
runtime: 'x-postplus-client-runtime',
|
|
9
|
-
|
|
9
|
+
skillsReleaseId: 'x-postplus-skills-release-id',
|
|
10
10
|
skillName: 'x-postplus-skill-name',
|
|
11
11
|
};
|
|
12
12
|
export async function buildPostPlusClientCompatibilityHeaders(input = {}) {
|
|
@@ -19,11 +19,11 @@ export async function buildPostPlusClientCompatibilityHeaders(input = {}) {
|
|
|
19
19
|
[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.contractVersion]: String(POSTPLUS_CLIENT_CONTRACT_VERSION),
|
|
20
20
|
[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.runtime]: POSTPLUS_CLIENT_RUNTIME,
|
|
21
21
|
};
|
|
22
|
-
const
|
|
22
|
+
const skillsReleaseId = config?.managedSkills?.releaseId?.trim();
|
|
23
23
|
const skillName = input.skillName?.trim();
|
|
24
|
-
if (
|
|
25
|
-
headers[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.
|
|
26
|
-
|
|
24
|
+
if (skillsReleaseId) {
|
|
25
|
+
headers[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.skillsReleaseId] =
|
|
26
|
+
skillsReleaseId;
|
|
27
27
|
}
|
|
28
28
|
if (skillName) {
|
|
29
29
|
headers[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.skillName] = skillName;
|
|
@@ -51,7 +51,7 @@ export function formatPostPlusClientUpgradeError(payload) {
|
|
|
51
51
|
? payload
|
|
52
52
|
: {};
|
|
53
53
|
const cliCommand = record.compatibility?.upgrade?.cli?.command ??
|
|
54
|
-
'npm install -g @postplus/cli';
|
|
54
|
+
'npm install -g @postplus/cli@latest';
|
|
55
55
|
const skillsCommand = record.compatibility?.upgrade?.skills?.command ?? 'postplus update';
|
|
56
56
|
const restart = record.compatibility?.upgrade?.restartAgentSession
|
|
57
57
|
? ' Then restart your agent session.'
|
|
@@ -67,3 +67,34 @@ export function formatPostPlusClientUpgradeError(payload) {
|
|
|
67
67
|
.filter(Boolean)
|
|
68
68
|
.join(' ');
|
|
69
69
|
}
|
|
70
|
+
export function formatPostPlusCloudReleaseInProgressError(payload) {
|
|
71
|
+
const record = payload && typeof payload === 'object' && !Array.isArray(payload)
|
|
72
|
+
? payload
|
|
73
|
+
: {};
|
|
74
|
+
return typeof record.error === 'string' && record.error.trim().length > 0
|
|
75
|
+
? record.error.trim()
|
|
76
|
+
: 'PostPlus Cloud is updating. Please retry in about one minute.';
|
|
77
|
+
}
|
|
78
|
+
export function formatPostPlusCompatibilityError(payload) {
|
|
79
|
+
if (isPostPlusClientUpgradePayload(payload)) {
|
|
80
|
+
return formatPostPlusClientUpgradeError(payload);
|
|
81
|
+
}
|
|
82
|
+
if (isPostPlusCloudReleaseInProgressPayload(payload)) {
|
|
83
|
+
return formatPostPlusCloudReleaseInProgressError(payload);
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
export function isPostPlusClientUpgradePayload(payload) {
|
|
88
|
+
return (payload &&
|
|
89
|
+
typeof payload === 'object' &&
|
|
90
|
+
!Array.isArray(payload) &&
|
|
91
|
+
'code' in payload &&
|
|
92
|
+
payload.code === 'postplus_client_upgrade_required');
|
|
93
|
+
}
|
|
94
|
+
export function isPostPlusCloudReleaseInProgressPayload(payload) {
|
|
95
|
+
return (payload &&
|
|
96
|
+
typeof payload === 'object' &&
|
|
97
|
+
!Array.isArray(payload) &&
|
|
98
|
+
'code' in payload &&
|
|
99
|
+
payload.code === 'postplus_cli_cloud_release_in_progress');
|
|
100
|
+
}
|
package/build/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveFreshRemoteAuth, } from './auth-session.js';
|
|
2
|
-
import { buildPostPlusClientCompatibilityHeaders,
|
|
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
5
|
function createPass(id, label, detail, severity = 'required') {
|
|
@@ -185,8 +185,9 @@ function readReadinessCheckFailureLabel(value) {
|
|
|
185
185
|
: 'unknown check';
|
|
186
186
|
}
|
|
187
187
|
function readErrorMessage(payload, fallback) {
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
const compatibilityError = formatPostPlusCompatibilityError(payload);
|
|
189
|
+
if (compatibilityError) {
|
|
190
|
+
return compatibilityError;
|
|
190
191
|
}
|
|
191
192
|
return typeof payload.error === 'string' && payload.error.trim().length > 0
|
|
192
193
|
? payload.error
|
package/build/index.js
CHANGED
|
@@ -3,13 +3,13 @@ import { formatAuthRefreshReport, refreshRemoteAuth, revokeRemoteAuthAndReport,
|
|
|
3
3
|
import { loginWithCloudHandoff } from './auth-login.js';
|
|
4
4
|
import { formatAuthValidateReport, validateRemoteAuth, } from './auth-validate.js';
|
|
5
5
|
import { clearAuthState, formatAuthStatusReport, generateAuthStatusReport, } from './auth.js';
|
|
6
|
+
import { readCurrentCliVersion } from './client-compatibility.js';
|
|
6
7
|
import { formatDoctorReport, generateDoctorReport } from './doctor.js';
|
|
7
8
|
import { assertConfigFilePermissions } from './local-state.js';
|
|
8
9
|
import { POSTPLUS_SKILLS_INSTALL_COMMAND, loadPublicSkillCatalog, } from './skill-catalog.js';
|
|
9
10
|
import { runPostPlusSkillUninstall, runPostPlusSkillUpdate, } from './skill-management.js';
|
|
10
11
|
import { formatStatusReport, generateStatusReport } from './status.js';
|
|
11
|
-
import {
|
|
12
|
-
import { refreshUpdateCheckCache } from './update-check.js';
|
|
12
|
+
import { refreshUpdateCheckCache, runCliSelfUpdateIfOutdated, } from './update-check.js';
|
|
13
13
|
function printAuthHelp() {
|
|
14
14
|
process.stdout.write(`PostPlus CLI — auth commands
|
|
15
15
|
|
|
@@ -103,6 +103,10 @@ async function runVersion() {
|
|
|
103
103
|
return 0;
|
|
104
104
|
}
|
|
105
105
|
async function runSkillUpdateCommand() {
|
|
106
|
+
const cliSelfUpdate = await runCliSelfUpdateIfOutdated();
|
|
107
|
+
if (cliSelfUpdate.updateAvailable) {
|
|
108
|
+
return cliSelfUpdate.exitCode ?? 1;
|
|
109
|
+
}
|
|
106
110
|
const exitCode = await runPostPlusSkillUpdate();
|
|
107
111
|
if (exitCode === 0) {
|
|
108
112
|
await refreshUpdateCheckCache().catch(() => { });
|
|
@@ -9,7 +9,7 @@ export async function generateLocalDependencyReport(options = {}) {
|
|
|
9
9
|
const checks = await Promise.all(requirements.map(({ dependency, skillIds }) => checkLocalDependency(dependency, skillIds, runDependencyCheck)));
|
|
10
10
|
return {
|
|
11
11
|
ok: checks.every((check) => check.ok),
|
|
12
|
-
|
|
12
|
+
releaseId: catalog.releaseId,
|
|
13
13
|
source: catalog.source,
|
|
14
14
|
requiredCount: checks.length,
|
|
15
15
|
checks,
|
|
@@ -17,11 +17,11 @@ export async function generateLocalDependencyReport(options = {}) {
|
|
|
17
17
|
}
|
|
18
18
|
export function formatLocalDependencyReport(report) {
|
|
19
19
|
if (report.requiredCount === 0) {
|
|
20
|
-
return `No local runtime dependencies are required by released PostPlus skills (${report.
|
|
20
|
+
return `No local runtime dependencies are required by released PostPlus skills (${report.releaseId}).`;
|
|
21
21
|
}
|
|
22
22
|
const missing = report.checks.filter((check) => !check.ok);
|
|
23
23
|
if (missing.length === 0) {
|
|
24
|
-
return `Ready (${report.requiredCount} local dependencies present; catalog ${report.
|
|
24
|
+
return `Ready (${report.requiredCount} local dependencies present; catalog ${report.releaseId})`;
|
|
25
25
|
}
|
|
26
26
|
return `Missing ${missing.length}/${report.requiredCount}: ${missing
|
|
27
27
|
.map((check) => `${check.dependency} for ${formatSkillList(check.skillIds)}`)
|
package/build/local-state.js
CHANGED
|
@@ -108,15 +108,15 @@ export async function readManagedSkillBaseline() {
|
|
|
108
108
|
const config = await readLocalConfig();
|
|
109
109
|
const managedSkills = config?.managedSkills;
|
|
110
110
|
if (!managedSkills ||
|
|
111
|
-
typeof managedSkills.
|
|
111
|
+
typeof managedSkills.releaseId !== 'string' ||
|
|
112
112
|
!Array.isArray(managedSkills.skillNames)) {
|
|
113
113
|
return {
|
|
114
|
-
|
|
114
|
+
releaseId: null,
|
|
115
115
|
skillNames: [],
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
return {
|
|
119
|
-
|
|
119
|
+
releaseId: managedSkills.releaseId,
|
|
120
120
|
skillNames: normalizeSkillNames(managedSkills.skillNames),
|
|
121
121
|
};
|
|
122
122
|
}
|
|
@@ -124,7 +124,7 @@ export async function writeManagedSkillBaseline(input) {
|
|
|
124
124
|
return updateLocalConfig((current) => ({
|
|
125
125
|
...(current ?? {}),
|
|
126
126
|
managedSkills: {
|
|
127
|
-
|
|
127
|
+
releaseId: input.releaseId,
|
|
128
128
|
skillNames: normalizeSkillNames(input.skillNames),
|
|
129
129
|
updatedAt: new Date().toISOString(),
|
|
130
130
|
},
|
package/build/skill-catalog.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const POSTPLUS_SKILLS_REPO = 'PostPlusAI/postplus-skills';
|
|
2
|
+
export const POSTPLUS_SKILLS_SOURCE_ENV = 'POSTPLUS_SKILLS_SOURCE';
|
|
3
|
+
export const POSTPLUS_SKILLS_CATALOG_URL_ENV = 'POSTPLUS_SKILLS_CATALOG_URL';
|
|
2
4
|
export const POSTPLUS_SKILLS_AGENT_TARGETS = [
|
|
3
5
|
'claude-code',
|
|
4
6
|
'codex',
|
|
@@ -9,12 +11,14 @@ export const POSTPLUS_SKILLS_AGENT_TARGETS = [
|
|
|
9
11
|
'trae-cn',
|
|
10
12
|
];
|
|
11
13
|
const POSTPLUS_SKILLS_AGENT_ARGS = POSTPLUS_SKILLS_AGENT_TARGETS.join(' ');
|
|
12
|
-
export const POSTPLUS_SKILLS_INSTALL_COMMAND =
|
|
13
|
-
export const POSTPLUS_SKILLS_LIST_COMMAND =
|
|
14
|
+
export const POSTPLUS_SKILLS_INSTALL_COMMAND = formatPostPlusSkillsInstallCommand();
|
|
15
|
+
export const POSTPLUS_SKILLS_LIST_COMMAND = formatPostPlusSkillsListCommand();
|
|
14
16
|
const POSTPLUS_SKILLS_INDEX_URL = 'https://raw.githubusercontent.com/PostPlusAI/postplus-skills/main/skills/INDEX.md';
|
|
15
17
|
const POSTPLUS_SKILLS_CATALOG_URL = 'https://raw.githubusercontent.com/PostPlusAI/postplus-skills/main/skills/catalog.json';
|
|
16
|
-
export async function loadPublicSkillCatalog(fetchFn = fetch) {
|
|
17
|
-
const
|
|
18
|
+
export async function loadPublicSkillCatalog(fetchFn = fetch, env = process.env) {
|
|
19
|
+
const catalogUrl = resolvePostPlusSkillsCatalogUrl(env);
|
|
20
|
+
const skillsSource = resolvePostPlusSkillsSource(env);
|
|
21
|
+
const response = await fetchFn(catalogUrl, {
|
|
18
22
|
headers: {
|
|
19
23
|
accept: 'application/json',
|
|
20
24
|
},
|
|
@@ -24,16 +28,29 @@ export async function loadPublicSkillCatalog(fetchFn = fetch) {
|
|
|
24
28
|
throw new Error(`Failed to load PostPlus skill catalog (${response.status}): ${response.statusText}`);
|
|
25
29
|
}
|
|
26
30
|
const raw = await response.text();
|
|
27
|
-
const payload = parseJsonResponse(raw,
|
|
31
|
+
const payload = parseJsonResponse(raw, catalogUrl);
|
|
28
32
|
const catalog = parsePublicSkillCatalog(payload);
|
|
29
33
|
return {
|
|
30
34
|
...catalog,
|
|
31
|
-
catalogUrl
|
|
35
|
+
catalogUrl,
|
|
32
36
|
indexUrl: POSTPLUS_SKILLS_INDEX_URL,
|
|
33
|
-
installCommand:
|
|
34
|
-
listCommand:
|
|
37
|
+
installCommand: formatPostPlusSkillsInstallCommand(skillsSource),
|
|
38
|
+
listCommand: formatPostPlusSkillsListCommand(skillsSource),
|
|
39
|
+
source: skillsSource,
|
|
35
40
|
};
|
|
36
41
|
}
|
|
42
|
+
export function resolvePostPlusSkillsSource(env = process.env) {
|
|
43
|
+
return env[POSTPLUS_SKILLS_SOURCE_ENV]?.trim() || POSTPLUS_SKILLS_REPO;
|
|
44
|
+
}
|
|
45
|
+
export function resolvePostPlusSkillsCatalogUrl(env = process.env) {
|
|
46
|
+
return env[POSTPLUS_SKILLS_CATALOG_URL_ENV]?.trim() || POSTPLUS_SKILLS_CATALOG_URL;
|
|
47
|
+
}
|
|
48
|
+
export function formatPostPlusSkillsInstallCommand(source = POSTPLUS_SKILLS_REPO) {
|
|
49
|
+
return `npx -y skills add ${source} --global --full-depth --skill '*' --agent ${POSTPLUS_SKILLS_AGENT_ARGS} --yes`;
|
|
50
|
+
}
|
|
51
|
+
export function formatPostPlusSkillsListCommand(source = POSTPLUS_SKILLS_REPO) {
|
|
52
|
+
return `npx -y skills add ${source} --list --full-depth`;
|
|
53
|
+
}
|
|
37
54
|
function parseJsonResponse(raw, url) {
|
|
38
55
|
try {
|
|
39
56
|
return JSON.parse(raw);
|
|
@@ -53,15 +70,15 @@ function parsePublicSkillCatalog(payload) {
|
|
|
53
70
|
throw new Error('PostPlus public skill catalog is invalid.');
|
|
54
71
|
}
|
|
55
72
|
const record = payload;
|
|
56
|
-
const
|
|
57
|
-
? record.
|
|
73
|
+
const releaseId = typeof record.releaseId === 'string' && record.releaseId.trim()
|
|
74
|
+
? record.releaseId.trim()
|
|
58
75
|
: null;
|
|
59
76
|
const source = typeof record.source === 'string' && record.source.trim()
|
|
60
77
|
? record.source.trim()
|
|
61
78
|
: null;
|
|
62
79
|
if (record.schemaVersion !== 1 ||
|
|
63
80
|
source !== POSTPLUS_SKILLS_REPO ||
|
|
64
|
-
!
|
|
81
|
+
!releaseId) {
|
|
65
82
|
throw new Error('PostPlus public skill catalog metadata is invalid.');
|
|
66
83
|
}
|
|
67
84
|
if (!Array.isArray(record.skills)) {
|
|
@@ -105,7 +122,7 @@ function parsePublicSkillCatalog(payload) {
|
|
|
105
122
|
throw new Error('PostPlus public skill catalog is invalid: no released skills were found.');
|
|
106
123
|
}
|
|
107
124
|
return {
|
|
108
|
-
|
|
125
|
+
releaseId,
|
|
109
126
|
skills,
|
|
110
127
|
source,
|
|
111
128
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { runCommand, runInteractiveCommand } from './command-runner.js';
|
|
2
1
|
import { writeCurrentCliVersionToLocalConfig } from './client-compatibility.js';
|
|
2
|
+
import { runCommand, runInteractiveCommand } from './command-runner.js';
|
|
3
3
|
import { clearManagedSkillBaseline, readManagedSkillBaseline, writeManagedSkillBaseline, } from './local-state.js';
|
|
4
|
-
import { POSTPLUS_SKILLS_AGENT_TARGETS,
|
|
4
|
+
import { POSTPLUS_SKILLS_AGENT_TARGETS, formatPostPlusSkillsInstallCommand, resolvePostPlusSkillsSource, loadPublicSkillCatalog, } from './skill-catalog.js';
|
|
5
5
|
const NPX_SKILLS = ['-y', 'skills'];
|
|
6
6
|
export async function runPostPlusSkillUpdate(dependencies = {
|
|
7
7
|
runInteractiveCommand,
|
|
@@ -24,7 +24,7 @@ export async function runPostPlusSkillUpdate(dependencies = {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
await writeManagedSkillBaseline({
|
|
27
|
-
|
|
27
|
+
releaseId: catalog.releaseId,
|
|
28
28
|
skillNames,
|
|
29
29
|
});
|
|
30
30
|
await writeCurrentCliVersionToLocalConfig();
|
|
@@ -66,14 +66,14 @@ export async function generateSkillInstallStatusReport(dependencies = {
|
|
|
66
66
|
return {
|
|
67
67
|
ok: missingSkills.length === 0,
|
|
68
68
|
error: null,
|
|
69
|
-
installCommand:
|
|
69
|
+
installCommand: formatPostPlusSkillsInstallCommand(catalog.source),
|
|
70
70
|
installedCount: installedNames.size,
|
|
71
|
-
|
|
71
|
+
managedSkillsReleaseId: baseline.releaseId,
|
|
72
72
|
missingSkills,
|
|
73
73
|
requiredCount: requiredSkills.size,
|
|
74
74
|
retiredManagedSkills,
|
|
75
75
|
scopes,
|
|
76
|
-
source:
|
|
76
|
+
source: catalog.source,
|
|
77
77
|
updateCommand: formatPostPlusSkillUpdateCommand(),
|
|
78
78
|
uninstallCommand: formatPostPlusSkillUninstallCommand(),
|
|
79
79
|
};
|
|
@@ -84,14 +84,14 @@ export async function generateSkillInstallStatusReport(dependencies = {
|
|
|
84
84
|
error: error instanceof Error
|
|
85
85
|
? error.message
|
|
86
86
|
: 'Failed to inspect installed PostPlus skills.',
|
|
87
|
-
installCommand:
|
|
87
|
+
installCommand: formatPostPlusSkillsInstallCommand(catalog.source),
|
|
88
88
|
installedCount: 0,
|
|
89
|
-
|
|
89
|
+
managedSkillsReleaseId: baseline.releaseId,
|
|
90
90
|
missingSkills: [...requiredSkills],
|
|
91
91
|
requiredCount: requiredSkills.size,
|
|
92
92
|
retiredManagedSkills,
|
|
93
93
|
scopes: [],
|
|
94
|
-
source:
|
|
94
|
+
source: catalog.source,
|
|
95
95
|
updateCommand: formatPostPlusSkillUpdateCommand(),
|
|
96
96
|
uninstallCommand: formatPostPlusSkillUninstallCommand(),
|
|
97
97
|
};
|
|
@@ -109,7 +109,7 @@ export function formatSkillInstallStatusReport(report) {
|
|
|
109
109
|
lines.push(`[FAIL] Installed released skills: ${report.installedCount}/${report.requiredCount}`);
|
|
110
110
|
}
|
|
111
111
|
lines.push(` Source: ${report.source}`);
|
|
112
|
-
lines.push(` Managed baseline: ${report.
|
|
112
|
+
lines.push(` Managed baseline: ${report.managedSkillsReleaseId ?? 'none'}`);
|
|
113
113
|
lines.push(` Scope: ${report.scopes.length > 0 ? report.scopes.join(', ') : 'none detected'}`);
|
|
114
114
|
if (report.retiredManagedSkills.length > 0) {
|
|
115
115
|
lines.push(` Retired managed skills: ${formatSkillList(report.retiredManagedSkills, 8)}`, ` Cleanup: ${report.updateCommand}`);
|
|
@@ -123,7 +123,22 @@ export function formatSkillInstallStatusReport(report) {
|
|
|
123
123
|
return lines.join('\n');
|
|
124
124
|
}
|
|
125
125
|
export function buildPostPlusSkillUpdateArgs(skillNames) {
|
|
126
|
-
|
|
126
|
+
if (skillNames.length === 0) {
|
|
127
|
+
throw new Error('PostPlus public skill catalog has no released skills.');
|
|
128
|
+
}
|
|
129
|
+
const skillsSource = resolvePostPlusSkillsSource();
|
|
130
|
+
return [
|
|
131
|
+
...NPX_SKILLS,
|
|
132
|
+
'add',
|
|
133
|
+
skillsSource,
|
|
134
|
+
'--global',
|
|
135
|
+
'--full-depth',
|
|
136
|
+
'--skill',
|
|
137
|
+
'*',
|
|
138
|
+
'--agent',
|
|
139
|
+
...POSTPLUS_SKILLS_AGENT_TARGETS,
|
|
140
|
+
'--yes',
|
|
141
|
+
];
|
|
127
142
|
}
|
|
128
143
|
export function buildPostPlusSkillUninstallArgs(skillNames) {
|
|
129
144
|
return [
|
package/build/update-check.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { readCurrentCliVersion } from './client-compatibility.js';
|
|
4
|
+
import { runInteractiveCommand as runDefaultInteractiveCommand, } from './command-runner.js';
|
|
4
5
|
import { getPostPlusConfigDir, readManagedSkillBaseline, } from './local-state.js';
|
|
5
6
|
import { POSTPLUS_SKILLS_REPO, loadPublicSkillCatalog, } from './skill-catalog.js';
|
|
6
7
|
const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
|
|
7
8
|
const UPDATE_CHECK_CACHE_FILE = 'update-check.json';
|
|
8
9
|
const NPM_PACKAGE_NAME = '@postplus/cli';
|
|
9
10
|
const NPM_LATEST_URL = `https://registry.npmjs.org/${encodeURIComponent(NPM_PACKAGE_NAME)}/latest`;
|
|
11
|
+
export const POSTPLUS_CLI_UPDATE_COMMAND = 'npm install -g @postplus/cli@latest';
|
|
12
|
+
const POSTPLUS_CLI_UPDATE_ARGS = ['install', '-g', '@postplus/cli@latest'];
|
|
10
13
|
export async function generateUpdateStatusReport(input = {}, dependencies = {
|
|
11
14
|
fetchFn: fetch,
|
|
12
15
|
}) {
|
|
@@ -20,14 +23,14 @@ export async function generateUpdateStatusReport(input = {}, dependencies = {
|
|
|
20
23
|
return buildUpdateReport({
|
|
21
24
|
cache,
|
|
22
25
|
currentVersion,
|
|
23
|
-
|
|
26
|
+
currentSkillsReleaseId: managedSkillBaseline.releaseId,
|
|
24
27
|
source: 'cache',
|
|
25
28
|
});
|
|
26
29
|
}
|
|
27
30
|
try {
|
|
28
|
-
const [latestCliVersion,
|
|
31
|
+
const [latestCliVersion, latestSkillsReleaseId] = await Promise.all([
|
|
29
32
|
fetchLatestCliVersion(dependencies.fetchFn),
|
|
30
|
-
|
|
33
|
+
fetchLatestSkillReleaseId(dependencies.fetchFn),
|
|
31
34
|
]);
|
|
32
35
|
const nextCache = {
|
|
33
36
|
checkedAt: new Date().toISOString(),
|
|
@@ -36,14 +39,14 @@ export async function generateUpdateStatusReport(input = {}, dependencies = {
|
|
|
36
39
|
latestVersion: latestCliVersion,
|
|
37
40
|
},
|
|
38
41
|
skills: {
|
|
39
|
-
|
|
42
|
+
latestReleaseId: latestSkillsReleaseId,
|
|
40
43
|
},
|
|
41
44
|
};
|
|
42
45
|
await writeUpdateCheckCache(nextCache);
|
|
43
46
|
return buildUpdateReport({
|
|
44
47
|
cache: nextCache,
|
|
45
48
|
currentVersion,
|
|
46
|
-
|
|
49
|
+
currentSkillsReleaseId: managedSkillBaseline.releaseId,
|
|
47
50
|
source: 'remote',
|
|
48
51
|
});
|
|
49
52
|
}
|
|
@@ -54,7 +57,7 @@ export async function generateUpdateStatusReport(input = {}, dependencies = {
|
|
|
54
57
|
...buildUpdateReport({
|
|
55
58
|
cache,
|
|
56
59
|
currentVersion,
|
|
57
|
-
|
|
60
|
+
currentSkillsReleaseId: managedSkillBaseline.releaseId,
|
|
58
61
|
source: 'cache',
|
|
59
62
|
}),
|
|
60
63
|
warning,
|
|
@@ -68,11 +71,11 @@ export async function generateUpdateStatusReport(input = {}, dependencies = {
|
|
|
68
71
|
currentVersion,
|
|
69
72
|
latestVersion: null,
|
|
70
73
|
updateAvailable: false,
|
|
71
|
-
updateCommand:
|
|
74
|
+
updateCommand: POSTPLUS_CLI_UPDATE_COMMAND,
|
|
72
75
|
},
|
|
73
76
|
skills: {
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
currentReleaseId: managedSkillBaseline.releaseId,
|
|
78
|
+
latestReleaseId: null,
|
|
76
79
|
updateAvailable: false,
|
|
77
80
|
updateCommand: 'postplus update',
|
|
78
81
|
},
|
|
@@ -85,6 +88,49 @@ export async function refreshUpdateCheckCache() {
|
|
|
85
88
|
force: true,
|
|
86
89
|
});
|
|
87
90
|
}
|
|
91
|
+
export async function runCliSelfUpdateIfOutdated(dependencies = {}) {
|
|
92
|
+
const fetchFn = dependencies.fetchFn ?? fetch;
|
|
93
|
+
const runInteractiveCommand = dependencies.runInteractiveCommand ?? runDefaultInteractiveCommand;
|
|
94
|
+
const writeOutput = dependencies.writeOutput ?? ((message) => process.stdout.write(message));
|
|
95
|
+
const currentVersion = await readCurrentCliVersion();
|
|
96
|
+
const latestVersion = await fetchLatestCliVersion(fetchFn);
|
|
97
|
+
if (compareVersions(latestVersion, currentVersion) <= 0) {
|
|
98
|
+
return {
|
|
99
|
+
command: POSTPLUS_CLI_UPDATE_COMMAND,
|
|
100
|
+
currentVersion,
|
|
101
|
+
exitCode: null,
|
|
102
|
+
latestVersion,
|
|
103
|
+
updateAvailable: false,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
writeOutput([
|
|
107
|
+
`PostPlus CLI ${currentVersion} is older than latest ${latestVersion}.`,
|
|
108
|
+
`Updating CLI: ${POSTPLUS_CLI_UPDATE_COMMAND}`,
|
|
109
|
+
'',
|
|
110
|
+
].join('\n'));
|
|
111
|
+
const exitCode = await runInteractiveCommand('npm', POSTPLUS_CLI_UPDATE_ARGS);
|
|
112
|
+
if (exitCode === 0) {
|
|
113
|
+
writeOutput([
|
|
114
|
+
`PostPlus CLI updated to ${latestVersion}.`,
|
|
115
|
+
'Re-run `postplus update` to update skills with the new CLI process.',
|
|
116
|
+
'',
|
|
117
|
+
].join('\n'));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
writeOutput([
|
|
121
|
+
`PostPlus CLI update failed with exit code ${exitCode}.`,
|
|
122
|
+
`Fix the npm install error, then run: ${POSTPLUS_CLI_UPDATE_COMMAND}`,
|
|
123
|
+
'',
|
|
124
|
+
].join('\n'));
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
command: POSTPLUS_CLI_UPDATE_COMMAND,
|
|
128
|
+
currentVersion,
|
|
129
|
+
exitCode,
|
|
130
|
+
latestVersion,
|
|
131
|
+
updateAvailable: true,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
88
134
|
export function formatUpdateStatusReport(report) {
|
|
89
135
|
const lines = ['PostPlus update status', ''];
|
|
90
136
|
const cliMarker = report.cli.updateAvailable ? '[WARN]' : '[PASS]';
|
|
@@ -93,8 +139,8 @@ export function formatUpdateStatusReport(report) {
|
|
|
93
139
|
lines.push(` Update: ${report.cli.updateCommand}`);
|
|
94
140
|
}
|
|
95
141
|
const skillMarker = report.skills.updateAvailable ? '[WARN]' : '[PASS]';
|
|
96
|
-
lines.push(`${skillMarker} Skills: ${report.skills.
|
|
97
|
-
? `release ${
|
|
142
|
+
lines.push(`${skillMarker} Skills: ${report.skills.latestReleaseId
|
|
143
|
+
? `release ${shortReleaseId(report.skills.latestReleaseId)}`
|
|
98
144
|
: 'release unknown'}`);
|
|
99
145
|
if (report.skills.updateAvailable) {
|
|
100
146
|
lines.push(` Update: ${report.skills.updateCommand}`);
|
|
@@ -115,12 +161,12 @@ function buildUpdateReport(input) {
|
|
|
115
161
|
latestVersion: input.cache.cli.latestVersion,
|
|
116
162
|
updateAvailable: compareVersions(input.cache.cli.latestVersion, input.currentVersion) >
|
|
117
163
|
0,
|
|
118
|
-
updateCommand:
|
|
164
|
+
updateCommand: POSTPLUS_CLI_UPDATE_COMMAND,
|
|
119
165
|
},
|
|
120
166
|
skills: {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
updateAvailable: input.cache.skills.
|
|
167
|
+
currentReleaseId: input.currentSkillsReleaseId,
|
|
168
|
+
latestReleaseId: input.cache.skills.latestReleaseId,
|
|
169
|
+
updateAvailable: input.cache.skills.latestReleaseId !== input.currentSkillsReleaseId,
|
|
124
170
|
updateCommand: 'postplus update',
|
|
125
171
|
},
|
|
126
172
|
warning: null,
|
|
@@ -143,12 +189,12 @@ async function fetchLatestCliVersion(fetchFn) {
|
|
|
143
189
|
}
|
|
144
190
|
return payload.version.trim();
|
|
145
191
|
}
|
|
146
|
-
async function
|
|
192
|
+
async function fetchLatestSkillReleaseId(fetchFn) {
|
|
147
193
|
try {
|
|
148
|
-
return (await loadPublicSkillCatalog(fetchFn)).
|
|
194
|
+
return (await loadPublicSkillCatalog(fetchFn)).releaseId;
|
|
149
195
|
}
|
|
150
196
|
catch (error) {
|
|
151
|
-
throw new Error(`Failed to check latest ${POSTPLUS_SKILLS_REPO}
|
|
197
|
+
throw new Error(`Failed to check latest ${POSTPLUS_SKILLS_REPO} releaseId: ${error instanceof Error ? error.message : String(error)}`);
|
|
152
198
|
}
|
|
153
199
|
}
|
|
154
200
|
async function readUpdateCheckCache() {
|
|
@@ -158,7 +204,7 @@ async function readUpdateCheckCache() {
|
|
|
158
204
|
if (typeof parsed.checkedAt !== 'string' ||
|
|
159
205
|
typeof parsed.cli?.currentVersion !== 'string' ||
|
|
160
206
|
typeof parsed.cli?.latestVersion !== 'string' ||
|
|
161
|
-
typeof parsed.skills?.
|
|
207
|
+
typeof parsed.skills?.latestReleaseId !== 'string') {
|
|
162
208
|
return null;
|
|
163
209
|
}
|
|
164
210
|
return parsed;
|
|
@@ -202,6 +248,6 @@ function parseVersion(value) {
|
|
|
202
248
|
.map((part) => Number.parseInt(part, 10))
|
|
203
249
|
.map((part) => (Number.isFinite(part) ? part : 0));
|
|
204
250
|
}
|
|
205
|
-
function
|
|
206
|
-
return
|
|
251
|
+
function shortReleaseId(releaseId) {
|
|
252
|
+
return releaseId.length > 12 ? releaseId.slice(0, 12) : releaseId;
|
|
207
253
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postplus/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
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.",
|