@postplus/cli 0.1.14 → 0.1.16
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 +8 -48
- package/build/auth-login.js +117 -166
- package/build/auth-session.js +137 -0
- package/build/auth-validate.js +20 -24
- package/build/auth.js +1 -13
- package/build/doctor.js +34 -26
- package/build/index.js +3 -6
- package/build/local-state.js +4 -44
- package/build/skill-catalog.js +11 -1
- package/build/skill-management.js +2 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Requires Node.js and npm.
|
|
|
33
33
|
```bash
|
|
34
34
|
npm install -g @postplus/cli
|
|
35
35
|
postplus auth login
|
|
36
|
-
npx -y skills add PostPlusAI/postplus-skills --full-depth --skill '*' --agent claude-code codex cursor --yes
|
|
36
|
+
npx -y skills add PostPlusAI/postplus-skills --full-depth --skill '*' --agent claude-code codex cursor github-copilot windsurf trae trae-cn --yes
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Useful checks:
|
package/build/auth-lifecycle.js
CHANGED
|
@@ -1,53 +1,16 @@
|
|
|
1
|
+
import { refreshRemoteAuthSession } from './auth-session.js';
|
|
1
2
|
import { clearAuthState, generateAuthStatusReport } from './auth.js';
|
|
2
3
|
import { requireHostedBaseUrl } from './hosted-release.js';
|
|
3
|
-
import { resolveAccessTokenState, resolveRefreshTokenState,
|
|
4
|
+
import { resolveAccessTokenState, resolveRefreshTokenState, } from './local-state.js';
|
|
4
5
|
export async function refreshRemoteAuth() {
|
|
5
|
-
const
|
|
6
|
-
requireHostedBaseUrl(),
|
|
7
|
-
resolveAccessTokenState(),
|
|
8
|
-
resolveRefreshTokenState(),
|
|
9
|
-
]);
|
|
10
|
-
if (!refreshTokenState.present || !refreshTokenState.value) {
|
|
11
|
-
throw new Error('Run `postplus auth login` before refreshing PostPlus auth.');
|
|
12
|
-
}
|
|
13
|
-
const response = await fetch(`${apiBaseUrl}/api/postplus-cli/auth/refresh`, {
|
|
14
|
-
method: 'POST',
|
|
15
|
-
headers: {
|
|
16
|
-
accept: 'application/json',
|
|
17
|
-
...(accessTokenState.value
|
|
18
|
-
? { authorization: `Bearer ${accessTokenState.value}` }
|
|
19
|
-
: {}),
|
|
20
|
-
'content-type': 'application/json',
|
|
21
|
-
},
|
|
22
|
-
body: JSON.stringify({
|
|
23
|
-
refreshToken: refreshTokenState.value,
|
|
24
|
-
}),
|
|
25
|
-
signal: AbortSignal.timeout(15000),
|
|
26
|
-
});
|
|
27
|
-
const payload = (await response.json());
|
|
28
|
-
if (!response.ok) {
|
|
29
|
-
throw new Error('error' in payload && typeof payload.error === 'string'
|
|
30
|
-
? payload.error
|
|
31
|
-
: 'Failed to refresh remote PostPlus auth.');
|
|
32
|
-
}
|
|
33
|
-
const successPayload = payload;
|
|
34
|
-
await setLocalSession({
|
|
35
|
-
accessToken: successPayload.accessToken,
|
|
36
|
-
accountId: successPayload.accountId,
|
|
37
|
-
apiBaseUrl,
|
|
38
|
-
refreshToken: successPayload.refreshToken,
|
|
39
|
-
sessionExpiresAt: successPayload.sessionExpiresAt,
|
|
40
|
-
userEmail: successPayload.userEmail,
|
|
41
|
-
userId: successPayload.userId,
|
|
42
|
-
});
|
|
6
|
+
const refreshed = await refreshRemoteAuthSession();
|
|
43
7
|
return {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
apiBaseUrl,
|
|
8
|
+
accountId: refreshed.accountId,
|
|
9
|
+
apiBaseUrl: refreshed.apiBaseUrl,
|
|
47
10
|
ok: true,
|
|
48
|
-
subscriptionStatus:
|
|
49
|
-
userEmail:
|
|
50
|
-
userId:
|
|
11
|
+
subscriptionStatus: refreshed.subscriptionStatus,
|
|
12
|
+
userEmail: refreshed.userEmail,
|
|
13
|
+
userId: refreshed.userId,
|
|
51
14
|
};
|
|
52
15
|
}
|
|
53
16
|
export function formatAuthRefreshReport(report) {
|
|
@@ -59,9 +22,6 @@ export function formatAuthRefreshReport(report) {
|
|
|
59
22
|
`Account: ${report.accountId}`,
|
|
60
23
|
`User: ${report.userEmail ?? report.userId}`,
|
|
61
24
|
`Subscription: ${report.subscriptionStatus ?? 'unknown'}`,
|
|
62
|
-
`Session expires at: ${typeof report.accessTokenExpiresAt === 'number'
|
|
63
|
-
? new Date(report.accessTokenExpiresAt * 1000).toISOString()
|
|
64
|
-
: 'unknown'}`,
|
|
65
25
|
].join('\n');
|
|
66
26
|
}
|
|
67
27
|
export async function revokeRemoteAuth() {
|
package/build/auth-login.js
CHANGED
|
@@ -1,61 +1,104 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { createServer } from 'node:http';
|
|
3
1
|
import { requireHostedBaseUrl } from './hosted-release.js';
|
|
4
2
|
import { setLocalSession } from './local-state.js';
|
|
5
|
-
export const
|
|
6
|
-
export async function
|
|
3
|
+
export const CLI_AUTH_LOGIN_TIMEOUT_MS = 30 * 60 * 1000;
|
|
4
|
+
export async function loginWithCloudHandoff() {
|
|
7
5
|
const baseUrl = await requireHostedBaseUrl();
|
|
8
|
-
const
|
|
9
|
-
allowedOrigin: new URL(baseUrl).origin,
|
|
10
|
-
});
|
|
11
|
-
const loginUrl = buildCliLoginUrl({
|
|
12
|
-
baseUrl,
|
|
13
|
-
bridgeUrl: handoff.bridgeUrl,
|
|
14
|
-
requestId: handoff.requestId,
|
|
15
|
-
});
|
|
6
|
+
const started = await startCloudAuthLogin(baseUrl);
|
|
16
7
|
process.stdout.write([
|
|
17
8
|
'PostPlus CLI login',
|
|
18
9
|
'',
|
|
19
10
|
'Open this URL in your browser to continue:',
|
|
20
|
-
|
|
11
|
+
started.verificationUrl,
|
|
12
|
+
'',
|
|
13
|
+
`Code: ${started.userCode}`,
|
|
21
14
|
'',
|
|
22
|
-
'Waiting for browser sign-in
|
|
15
|
+
'Waiting for browser sign-in...',
|
|
23
16
|
'',
|
|
24
17
|
].join('\n'));
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
18
|
+
const handoffPayload = await waitForCloudAuthLogin({
|
|
19
|
+
apiBaseUrl: baseUrl,
|
|
20
|
+
expiresAt: started.expiresAt,
|
|
21
|
+
pollIntervalSeconds: started.pollIntervalSeconds,
|
|
22
|
+
pollSecret: started.pollSecret,
|
|
23
|
+
requestId: started.requestId,
|
|
24
|
+
});
|
|
25
|
+
const validated = await validateCliSession({
|
|
26
|
+
accessToken: handoffPayload.accessToken,
|
|
27
|
+
apiBaseUrl: baseUrl,
|
|
28
|
+
});
|
|
29
|
+
await setLocalSession({
|
|
30
|
+
accessToken: handoffPayload.accessToken,
|
|
31
|
+
accountId: validated.accountId,
|
|
32
|
+
apiBaseUrl: baseUrl,
|
|
33
|
+
refreshToken: handoffPayload.refreshToken,
|
|
34
|
+
sessionExpiresAt: validated.sessionExpiresAt ?? handoffPayload.sessionExpiresAt ?? null,
|
|
35
|
+
userEmail: validated.userEmail,
|
|
36
|
+
userId: validated.userId,
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
accountId: validated.accountId,
|
|
40
|
+
apiBaseUrl: baseUrl,
|
|
41
|
+
ok: true,
|
|
42
|
+
userEmail: validated.userEmail,
|
|
43
|
+
userId: validated.userId,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export async function startCloudAuthLogin(apiBaseUrl) {
|
|
47
|
+
const response = await fetch(`${apiBaseUrl}/api/postplus-cli/auth/login/start`, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: {
|
|
50
|
+
accept: 'application/json',
|
|
51
|
+
},
|
|
52
|
+
signal: AbortSignal.timeout(15000),
|
|
53
|
+
});
|
|
54
|
+
const payload = (await response.json());
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
throw new Error(formatRemoteAuthLoginError(payload));
|
|
51
57
|
}
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
if (!isCliAuthLoginStartSuccessPayload(payload)) {
|
|
59
|
+
throw new Error('PostPlus CLI sign-in start returned incomplete data.');
|
|
60
|
+
}
|
|
61
|
+
return payload;
|
|
62
|
+
}
|
|
63
|
+
async function waitForCloudAuthLogin(input) {
|
|
64
|
+
const expiresAtMs = Date.parse(input.expiresAt);
|
|
65
|
+
const deadlineMs = Number.isFinite(expiresAtMs)
|
|
66
|
+
? expiresAtMs
|
|
67
|
+
: Date.now() + CLI_AUTH_LOGIN_TIMEOUT_MS;
|
|
68
|
+
const pollIntervalMs = Math.max(1000, input.pollIntervalSeconds * 1000);
|
|
69
|
+
while (Date.now() < deadlineMs) {
|
|
70
|
+
const payload = await pollCloudAuthLogin(input);
|
|
71
|
+
if (payload.status === 'completed') {
|
|
72
|
+
return payload;
|
|
73
|
+
}
|
|
74
|
+
await delay(pollIntervalMs);
|
|
54
75
|
}
|
|
76
|
+
throw new Error('Timed out waiting for the cloud sign-in handoff.');
|
|
55
77
|
}
|
|
56
|
-
function
|
|
57
|
-
const
|
|
58
|
-
|
|
78
|
+
export async function pollCloudAuthLogin(input) {
|
|
79
|
+
const response = await fetch(`${input.apiBaseUrl}/api/postplus-cli/auth/login/poll`, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
accept: 'application/json',
|
|
83
|
+
'content-type': 'application/json',
|
|
84
|
+
},
|
|
85
|
+
body: JSON.stringify({
|
|
86
|
+
pollSecret: input.pollSecret,
|
|
87
|
+
requestId: input.requestId,
|
|
88
|
+
}),
|
|
89
|
+
signal: AbortSignal.timeout(15000),
|
|
90
|
+
});
|
|
91
|
+
const payload = (await response.json());
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw new Error(formatRemoteAuthLoginError(payload));
|
|
94
|
+
}
|
|
95
|
+
if (isCliAuthLoginCompletedPayload(payload)) {
|
|
96
|
+
return payload;
|
|
97
|
+
}
|
|
98
|
+
if (isCliAuthLoginPendingPayload(payload)) {
|
|
99
|
+
return payload;
|
|
100
|
+
}
|
|
101
|
+
throw new Error('PostPlus CLI sign-in poll returned incomplete data.');
|
|
59
102
|
}
|
|
60
103
|
export async function validateCliSession(input) {
|
|
61
104
|
const response = await fetch(`${input.apiBaseUrl}/api/postplus-cli/auth/whoami`, {
|
|
@@ -85,125 +128,33 @@ export function formatCliSessionAuthError(payload) {
|
|
|
85
128
|
}
|
|
86
129
|
return 'Failed to validate the browser session for PostPlus CLI.';
|
|
87
130
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
if (request.method !== 'POST' || request.url !== '/handoff') {
|
|
120
|
-
response.writeHead(404);
|
|
121
|
-
response.end();
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (!allowOrigin) {
|
|
125
|
-
response.writeHead(403);
|
|
126
|
-
response.end();
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const chunks = [];
|
|
130
|
-
request.on('data', (chunk) => {
|
|
131
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
132
|
-
});
|
|
133
|
-
request.on('end', () => {
|
|
134
|
-
try {
|
|
135
|
-
const payload = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
136
|
-
if (payload.requestId !== requestId) {
|
|
137
|
-
throw new Error('Mismatched CLI auth handoff request id.');
|
|
138
|
-
}
|
|
139
|
-
response.writeHead(200, {
|
|
140
|
-
'Access-Control-Allow-Origin': allowOrigin,
|
|
141
|
-
'Content-Type': 'application/json',
|
|
142
|
-
Vary: 'Origin',
|
|
143
|
-
});
|
|
144
|
-
response.end(JSON.stringify({ ok: true }));
|
|
145
|
-
if (!settled) {
|
|
146
|
-
settled = true;
|
|
147
|
-
resolvePayload?.(payload);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
151
|
-
response.writeHead(400, {
|
|
152
|
-
'Access-Control-Allow-Origin': allowOrigin,
|
|
153
|
-
'Content-Type': 'application/json',
|
|
154
|
-
Vary: 'Origin',
|
|
155
|
-
});
|
|
156
|
-
response.end(JSON.stringify({
|
|
157
|
-
error: error instanceof Error
|
|
158
|
-
? error.message
|
|
159
|
-
: 'Invalid CLI auth handoff payload.',
|
|
160
|
-
}));
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
server.on('error', (error) => {
|
|
165
|
-
if (!settled) {
|
|
166
|
-
settled = true;
|
|
167
|
-
reject(error);
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
rejectPayload?.(error);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
server.listen(0, '127.0.0.1', () => {
|
|
174
|
-
const address = server.address();
|
|
175
|
-
if (!address || typeof address === 'string') {
|
|
176
|
-
const error = new Error('Failed to bind the local CLI auth bridge.');
|
|
177
|
-
if (!settled) {
|
|
178
|
-
settled = true;
|
|
179
|
-
reject(error);
|
|
180
|
-
}
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
cleanupTimer = setTimeout(() => {
|
|
184
|
-
if (!settled) {
|
|
185
|
-
settled = true;
|
|
186
|
-
rejectPayload?.(new Error('Timed out waiting for the browser sign-in handoff.'));
|
|
187
|
-
}
|
|
188
|
-
server.close();
|
|
189
|
-
}, CLI_AUTH_HANDOFF_TIMEOUT_MS);
|
|
190
|
-
resolve({
|
|
191
|
-
bridgeUrl: `http://127.0.0.1:${address.port}/handoff`,
|
|
192
|
-
close: async () => new Promise((innerResolve, innerReject) => {
|
|
193
|
-
if (cleanupTimer) {
|
|
194
|
-
clearTimeout(cleanupTimer);
|
|
195
|
-
}
|
|
196
|
-
server.close((error) => {
|
|
197
|
-
if (error) {
|
|
198
|
-
innerReject(error);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
innerResolve();
|
|
202
|
-
});
|
|
203
|
-
}),
|
|
204
|
-
requestId,
|
|
205
|
-
waitForPayload: () => payloadPromise,
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
});
|
|
131
|
+
function isCliAuthLoginStartSuccessPayload(payload) {
|
|
132
|
+
return ('requestId' in payload &&
|
|
133
|
+
typeof payload.requestId === 'string' &&
|
|
134
|
+
typeof payload.pollSecret === 'string' &&
|
|
135
|
+
typeof payload.userCode === 'string' &&
|
|
136
|
+
typeof payload.verificationUrl === 'string' &&
|
|
137
|
+
typeof payload.expiresAt === 'string' &&
|
|
138
|
+
typeof payload.pollIntervalSeconds === 'number');
|
|
139
|
+
}
|
|
140
|
+
function isCliAuthLoginCompletedPayload(payload) {
|
|
141
|
+
return ('status' in payload &&
|
|
142
|
+
payload.status === 'completed' &&
|
|
143
|
+
typeof payload.accessToken === 'string' &&
|
|
144
|
+
typeof payload.refreshToken === 'string' &&
|
|
145
|
+
typeof payload.accountId === 'string' &&
|
|
146
|
+
typeof payload.userId === 'string');
|
|
147
|
+
}
|
|
148
|
+
function isCliAuthLoginPendingPayload(payload) {
|
|
149
|
+
return 'status' in payload && payload.status === 'pending';
|
|
150
|
+
}
|
|
151
|
+
function formatRemoteAuthLoginError(payload) {
|
|
152
|
+
return 'error' in payload &&
|
|
153
|
+
typeof payload.error === 'string' &&
|
|
154
|
+
payload.error.trim().length > 0
|
|
155
|
+
? payload.error
|
|
156
|
+
: 'PostPlus CLI sign-in failed.';
|
|
157
|
+
}
|
|
158
|
+
function delay(ms) {
|
|
159
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
209
160
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { requireHostedBaseUrl } from './hosted-release.js';
|
|
2
|
+
import { readLocalConfig, resolveAccessTokenState, resolveRefreshTokenState, setLocalSession, } from './local-state.js';
|
|
3
|
+
export const AUTH_SESSION_REFRESH_LEEWAY_SECONDS = 60;
|
|
4
|
+
export async function resolveFreshRemoteAuth(options = {}) {
|
|
5
|
+
const [apiBaseUrl, accessTokenState, refreshTokenState, config] = await Promise.all([
|
|
6
|
+
requireHostedBaseUrl(),
|
|
7
|
+
resolveAccessTokenState(),
|
|
8
|
+
resolveRefreshTokenState(),
|
|
9
|
+
readLocalConfig(),
|
|
10
|
+
]);
|
|
11
|
+
if (!refreshTokenState.present || !refreshTokenState.value) {
|
|
12
|
+
if (!accessTokenState.present || !accessTokenState.value) {
|
|
13
|
+
throw new Error('Run `postplus auth login` before validating PostPlus auth.');
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
accessToken: accessTokenState.value,
|
|
17
|
+
apiBaseUrl,
|
|
18
|
+
refreshed: false,
|
|
19
|
+
source: 'config',
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const existingAccessToken = accessTokenState.value;
|
|
23
|
+
const decodedTokenExpiresAt = existingAccessToken
|
|
24
|
+
? decodeAccessTokenExpiration(existingAccessToken)
|
|
25
|
+
: null;
|
|
26
|
+
const tokenExpiresAt = typeof decodedTokenExpiresAt === 'number'
|
|
27
|
+
? decodedTokenExpiresAt
|
|
28
|
+
: typeof config?.sessionExpiresAt === 'number'
|
|
29
|
+
? config.sessionExpiresAt
|
|
30
|
+
: null;
|
|
31
|
+
const shouldRefresh = options.forceRefresh === true ||
|
|
32
|
+
!accessTokenState.present ||
|
|
33
|
+
!accessTokenState.value ||
|
|
34
|
+
isExpiringSoon(tokenExpiresAt);
|
|
35
|
+
if (!shouldRefresh) {
|
|
36
|
+
if (!existingAccessToken) {
|
|
37
|
+
throw new Error('Run `postplus auth login` before validating PostPlus auth.');
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
accessToken: existingAccessToken,
|
|
41
|
+
apiBaseUrl,
|
|
42
|
+
refreshed: false,
|
|
43
|
+
source: 'config',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const refreshed = await refreshRemoteAuthSession({
|
|
47
|
+
accessToken: accessTokenState.value,
|
|
48
|
+
apiBaseUrl,
|
|
49
|
+
refreshToken: refreshTokenState.value,
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
accessToken: refreshed.accessToken,
|
|
53
|
+
apiBaseUrl,
|
|
54
|
+
refreshed: true,
|
|
55
|
+
source: 'config',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export async function refreshRemoteAuthSession(input) {
|
|
59
|
+
const [apiBaseUrl, accessTokenState, refreshTokenState] = await Promise.all([
|
|
60
|
+
input?.apiBaseUrl ?? requireHostedBaseUrl(),
|
|
61
|
+
input?.accessToken === undefined ? resolveAccessTokenState() : null,
|
|
62
|
+
input?.refreshToken === undefined ? resolveRefreshTokenState() : null,
|
|
63
|
+
]);
|
|
64
|
+
const accessToken = input?.accessToken === undefined
|
|
65
|
+
? accessTokenState?.value
|
|
66
|
+
: input.accessToken;
|
|
67
|
+
const refreshToken = input?.refreshToken === undefined
|
|
68
|
+
? refreshTokenState?.value
|
|
69
|
+
: input.refreshToken;
|
|
70
|
+
if (!refreshToken) {
|
|
71
|
+
throw new Error('Run `postplus auth login` before refreshing PostPlus auth.');
|
|
72
|
+
}
|
|
73
|
+
const response = await fetch(`${apiBaseUrl}/api/postplus-cli/auth/refresh`, {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: {
|
|
76
|
+
accept: 'application/json',
|
|
77
|
+
...(accessToken ? { authorization: `Bearer ${accessToken}` } : {}),
|
|
78
|
+
'content-type': 'application/json',
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
refreshToken,
|
|
82
|
+
}),
|
|
83
|
+
signal: AbortSignal.timeout(15000),
|
|
84
|
+
});
|
|
85
|
+
const payload = (await response.json());
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error('error' in payload && typeof payload.error === 'string'
|
|
88
|
+
? payload.error
|
|
89
|
+
: 'Failed to refresh remote PostPlus auth.');
|
|
90
|
+
}
|
|
91
|
+
if (!isRemoteAuthRefreshSuccessPayload(payload)) {
|
|
92
|
+
throw new Error('PostPlus auth refresh returned incomplete session tokens.');
|
|
93
|
+
}
|
|
94
|
+
await setLocalSession({
|
|
95
|
+
accessToken: payload.accessToken,
|
|
96
|
+
accountId: payload.accountId,
|
|
97
|
+
apiBaseUrl,
|
|
98
|
+
refreshToken: payload.refreshToken,
|
|
99
|
+
sessionExpiresAt: payload.sessionExpiresAt,
|
|
100
|
+
userEmail: payload.userEmail,
|
|
101
|
+
userId: payload.userId,
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
...payload,
|
|
105
|
+
apiBaseUrl,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export function decodeAccessTokenExpiration(accessToken) {
|
|
109
|
+
try {
|
|
110
|
+
const [, payload] = accessToken.split('.');
|
|
111
|
+
if (!payload) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const decoded = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
|
|
115
|
+
return typeof decoded.exp === 'number' ? decoded.exp : null;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function isExpiringSoon(expiresAt) {
|
|
122
|
+
if (typeof expiresAt !== 'number' || !Number.isFinite(expiresAt)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const nowSeconds = Math.floor(Date.now() / 1_000);
|
|
126
|
+
return expiresAt - nowSeconds <= AUTH_SESSION_REFRESH_LEEWAY_SECONDS;
|
|
127
|
+
}
|
|
128
|
+
function isRemoteAuthRefreshSuccessPayload(payload) {
|
|
129
|
+
return (typeof payload === 'object' &&
|
|
130
|
+
payload !== null &&
|
|
131
|
+
typeof payload.accessToken === 'string' &&
|
|
132
|
+
payload.accessToken.trim().length > 0 &&
|
|
133
|
+
typeof payload.refreshToken === 'string' &&
|
|
134
|
+
payload.refreshToken.trim().length > 0 &&
|
|
135
|
+
typeof payload.accountId === 'string' &&
|
|
136
|
+
typeof payload.userId === 'string');
|
|
137
|
+
}
|
package/build/auth-validate.js
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolveAccessTokenState } from './local-state.js';
|
|
1
|
+
import { resolveFreshRemoteAuth } from './auth-session.js';
|
|
3
2
|
export async function validateRemoteAuth() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
let auth = await resolveFreshRemoteAuth();
|
|
4
|
+
let response = await fetchWhoami(auth);
|
|
5
|
+
if (response.status === 401) {
|
|
6
|
+
auth = await resolveFreshRemoteAuth({
|
|
7
|
+
forceRefresh: true,
|
|
8
|
+
});
|
|
9
|
+
response = await fetchWhoami(auth);
|
|
10
10
|
}
|
|
11
|
-
const response = await fetch(`${apiBaseUrl}/api/postplus-cli/auth/whoami`, {
|
|
12
|
-
method: 'GET',
|
|
13
|
-
headers: {
|
|
14
|
-
accept: 'application/json',
|
|
15
|
-
authorization: `Bearer ${accessTokenState.value}`,
|
|
16
|
-
},
|
|
17
|
-
signal: AbortSignal.timeout(15000),
|
|
18
|
-
});
|
|
19
11
|
const payload = (await response.json());
|
|
20
12
|
if (!response.ok) {
|
|
21
13
|
throw new Error('error' in payload && typeof payload.error === 'string'
|
|
@@ -25,12 +17,9 @@ export async function validateRemoteAuth() {
|
|
|
25
17
|
const successPayload = payload;
|
|
26
18
|
return {
|
|
27
19
|
accountId: successPayload.accountId,
|
|
28
|
-
apiBaseUrl,
|
|
20
|
+
apiBaseUrl: auth.apiBaseUrl,
|
|
29
21
|
ok: true,
|
|
30
|
-
|
|
31
|
-
source: accessTokenState.source === 'missing'
|
|
32
|
-
? 'config'
|
|
33
|
-
: accessTokenState.source,
|
|
22
|
+
source: auth.source,
|
|
34
23
|
subscriptionStatus: successPayload.subscriptionStatus,
|
|
35
24
|
userEmail: successPayload.userEmail,
|
|
36
25
|
userId: successPayload.userId,
|
|
@@ -45,8 +34,15 @@ export function formatAuthValidateReport(report) {
|
|
|
45
34
|
`Account: ${report.accountId}`,
|
|
46
35
|
`User: ${report.userEmail ?? report.userId}`,
|
|
47
36
|
`Subscription: ${report.subscriptionStatus ?? 'unknown'}`,
|
|
48
|
-
`Session expires at: ${typeof report.sessionExpiresAt === 'number'
|
|
49
|
-
? new Date(report.sessionExpiresAt * 1000).toISOString()
|
|
50
|
-
: 'unknown'}`,
|
|
51
37
|
].join('\n');
|
|
52
38
|
}
|
|
39
|
+
function fetchWhoami(input) {
|
|
40
|
+
return fetch(`${input.apiBaseUrl}/api/postplus-cli/auth/whoami`, {
|
|
41
|
+
method: 'GET',
|
|
42
|
+
headers: {
|
|
43
|
+
accept: 'application/json',
|
|
44
|
+
authorization: `Bearer ${input.accessToken}`,
|
|
45
|
+
},
|
|
46
|
+
signal: AbortSignal.timeout(15000),
|
|
47
|
+
});
|
|
48
|
+
}
|
package/build/auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clearLocalAuthState, getPostPlusConfigPath, hasLocalConfigFile, maskSecret, readLocalConfig, resolveApiBaseUrlState, resolveLocalSessionState,
|
|
1
|
+
import { clearLocalAuthState, getPostPlusConfigPath, hasLocalConfigFile, maskSecret, readLocalConfig, resolveApiBaseUrlState, resolveLocalSessionState, setLocalApiBaseUrl, } from './local-state.js';
|
|
2
2
|
export async function generateAuthStatusReport() {
|
|
3
3
|
const [sessionState, apiBaseUrlState, configExists, config] = await Promise.all([
|
|
4
4
|
resolveLocalSessionState(),
|
|
@@ -32,7 +32,6 @@ export async function generateAuthStatusReport() {
|
|
|
32
32
|
present: sessionState.refreshToken.present,
|
|
33
33
|
maskedValue: maskSecret(sessionState.refreshToken.value),
|
|
34
34
|
},
|
|
35
|
-
sessionExpiresAt: sessionState.expiresAt,
|
|
36
35
|
};
|
|
37
36
|
}
|
|
38
37
|
export function formatAuthStatusReport(report) {
|
|
@@ -60,20 +59,9 @@ export function formatAuthStatusReport(report) {
|
|
|
60
59
|
: `[PASS] local config path: ${report.config.path}`);
|
|
61
60
|
lines.push(` Account: ${report.config.accountId ?? 'not bound'}`);
|
|
62
61
|
lines.push(` User: ${report.config.userEmail ?? report.config.userId ?? 'not bound'}`);
|
|
63
|
-
lines.push(` Session expires at: ${typeof report.sessionExpiresAt === 'number'
|
|
64
|
-
? new Date(report.sessionExpiresAt * 1000).toISOString()
|
|
65
|
-
: 'unknown'}`);
|
|
66
62
|
lines.push('', report.ok ? 'Auth status OK.' : 'Auth status incomplete.');
|
|
67
63
|
return lines.join('\n');
|
|
68
64
|
}
|
|
69
|
-
export async function configureAccessToken(accessToken) {
|
|
70
|
-
await setLocalAccessToken(accessToken);
|
|
71
|
-
return generateAuthStatusReport();
|
|
72
|
-
}
|
|
73
|
-
export async function configureRefreshToken(refreshToken) {
|
|
74
|
-
await setLocalRefreshToken(refreshToken);
|
|
75
|
-
return generateAuthStatusReport();
|
|
76
|
-
}
|
|
77
65
|
export async function configureApiBaseUrl(apiBaseUrl) {
|
|
78
66
|
await setLocalApiBaseUrl(apiBaseUrl);
|
|
79
67
|
return generateAuthStatusReport();
|
package/build/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { resolveFreshRemoteAuth, } from './auth-session.js';
|
|
1
2
|
import { resolveHostedBaseUrl } from './hosted-release.js';
|
|
2
|
-
import { resolveAccessTokenState } from './local-state.js';
|
|
3
3
|
function createPass(id, label, detail) {
|
|
4
4
|
return {
|
|
5
5
|
id,
|
|
@@ -22,25 +22,24 @@ export async function generateDoctorReport() {
|
|
|
22
22
|
const checks = [
|
|
23
23
|
createPass('hosted_base_url', 'PostPlus Cloud', `Using ${hostedBaseUrl ?? 'https://postplus.io'}`),
|
|
24
24
|
];
|
|
25
|
-
const accessToken = await resolveAccessTokenState();
|
|
26
25
|
if (!hostedBaseUrl) {
|
|
27
26
|
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`.'));
|
|
28
27
|
return buildDoctorReport(checks);
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const auth = await resolveFreshRemoteAuth().catch((error) => {
|
|
30
|
+
const message = error instanceof Error
|
|
31
|
+
? error.message
|
|
32
|
+
: 'No PostPlus CLI session is configured.';
|
|
33
|
+
checks.push(createFail('remote_auth', 'Remote auth', message, 'Run `postplus auth login`.'));
|
|
34
|
+
return null;
|
|
35
|
+
});
|
|
36
|
+
if (!auth) {
|
|
32
37
|
return buildDoctorReport(checks);
|
|
33
38
|
}
|
|
34
|
-
const authCheck = await checkRemoteAuth(
|
|
35
|
-
accessToken: accessToken.value,
|
|
36
|
-
hostedBaseUrl,
|
|
37
|
-
});
|
|
39
|
+
const authCheck = await checkRemoteAuth(auth);
|
|
38
40
|
checks.push(authCheck);
|
|
39
41
|
if (authCheck.status === 'pass') {
|
|
40
|
-
checks.push(await checkHostedCapabilities(
|
|
41
|
-
accessToken: accessToken.value,
|
|
42
|
-
hostedBaseUrl,
|
|
43
|
-
}));
|
|
42
|
+
checks.push(await checkHostedCapabilities(auth));
|
|
44
43
|
}
|
|
45
44
|
return buildDoctorReport(checks);
|
|
46
45
|
}
|
|
@@ -52,13 +51,13 @@ function buildDoctorReport(checks) {
|
|
|
52
51
|
}
|
|
53
52
|
async function checkRemoteAuth(input) {
|
|
54
53
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
}
|
|
54
|
+
let response = await requestWithAuth(input, '/api/postplus-cli/auth/whoami');
|
|
55
|
+
if (response.status === 401) {
|
|
56
|
+
const refreshedAuth = await resolveFreshRemoteAuth({
|
|
57
|
+
forceRefresh: true,
|
|
58
|
+
});
|
|
59
|
+
response = await requestWithAuth(refreshedAuth, '/api/postplus-cli/auth/whoami');
|
|
60
|
+
}
|
|
62
61
|
const payload = (await response.json());
|
|
63
62
|
if (!response.ok) {
|
|
64
63
|
return createFail('remote_auth', 'Remote auth', readErrorMessage(payload, 'PostPlus Cloud rejected the CLI session.'), 'Run `postplus auth login`.');
|
|
@@ -82,13 +81,13 @@ async function checkRemoteAuth(input) {
|
|
|
82
81
|
}
|
|
83
82
|
async function checkHostedCapabilities(input) {
|
|
84
83
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
}
|
|
84
|
+
let response = await requestWithAuth(input, '/api/postplus-cli/hosted/readiness');
|
|
85
|
+
if (response.status === 401) {
|
|
86
|
+
const refreshedAuth = await resolveFreshRemoteAuth({
|
|
87
|
+
forceRefresh: true,
|
|
88
|
+
});
|
|
89
|
+
response = await requestWithAuth(refreshedAuth, '/api/postplus-cli/hosted/readiness');
|
|
90
|
+
}
|
|
92
91
|
const payload = (await response.json());
|
|
93
92
|
if (!response.ok) {
|
|
94
93
|
return createFail('hosted_capabilities', 'Hosted capabilities', readErrorMessage(payload, 'PostPlus Cloud hosted readiness check failed.'));
|
|
@@ -132,6 +131,15 @@ function readErrorMessage(payload, fallback) {
|
|
|
132
131
|
? payload.error
|
|
133
132
|
: fallback;
|
|
134
133
|
}
|
|
134
|
+
function requestWithAuth(input, path) {
|
|
135
|
+
return fetch(`${input.apiBaseUrl}${path}`, {
|
|
136
|
+
headers: {
|
|
137
|
+
accept: 'application/json',
|
|
138
|
+
authorization: `Bearer ${input.accessToken}`,
|
|
139
|
+
},
|
|
140
|
+
signal: AbortSignal.timeout(15000),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
135
143
|
export function formatDoctorReport(report) {
|
|
136
144
|
const lines = ['PostPlus CLI doctor', ''];
|
|
137
145
|
for (const check of report.checks) {
|
package/build/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { formatAuthRefreshReport, refreshRemoteAuth, revokeRemoteAuthAndReport, } from './auth-lifecycle.js';
|
|
3
|
-
import {
|
|
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 { formatDoctorReport, generateDoctorReport
|
|
6
|
+
import { formatDoctorReport, generateDoctorReport } from './doctor.js';
|
|
7
7
|
import { assertConfigFilePermissions } from './local-state.js';
|
|
8
8
|
import { POSTPLUS_SKILLS_INSTALL_COMMAND, loadPublicSkillCatalog, } from './skill-catalog.js';
|
|
9
9
|
import { runPostPlusSkillUninstall, runPostPlusSkillUpdate, } from './skill-management.js';
|
|
@@ -140,16 +140,13 @@ async function runAuthRevoke(json) {
|
|
|
140
140
|
return 0;
|
|
141
141
|
}
|
|
142
142
|
async function runAuthLogin() {
|
|
143
|
-
const report = await
|
|
143
|
+
const report = await loginWithCloudHandoff();
|
|
144
144
|
process.stdout.write([
|
|
145
145
|
'',
|
|
146
146
|
'PostPlus CLI login complete.',
|
|
147
147
|
`Account: ${report.accountId}`,
|
|
148
148
|
`PostPlus Cloud: ${report.apiBaseUrl}`,
|
|
149
149
|
`User: ${report.userEmail ?? 'unknown'}`,
|
|
150
|
-
`Session expires at: ${typeof report.sessionExpiresAt === 'number'
|
|
151
|
-
? new Date(report.sessionExpiresAt * 1000).toISOString()
|
|
152
|
-
: 'unknown'}`,
|
|
153
150
|
'',
|
|
154
151
|
].join('\n'));
|
|
155
152
|
return report.ok ? 0 : 1;
|
package/build/local-state.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { constants as fsConstants } from 'node:fs';
|
|
2
|
-
import { access, chmod, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { access, chmod, mkdir, readFile, stat, writeFile, } from 'node:fs/promises';
|
|
3
3
|
import { homedir, platform } from 'node:os';
|
|
4
4
|
import { dirname, join, resolve } from 'node:path';
|
|
5
5
|
export const DEFAULT_POSTPLUS_API_BASE_URL = 'https://postplus.io';
|
|
@@ -103,26 +103,6 @@ export async function clearLocalAuthState() {
|
|
|
103
103
|
return next;
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
|
-
export async function setLocalAccessToken(accessToken) {
|
|
107
|
-
const normalizedAccessToken = accessToken.trim();
|
|
108
|
-
if (normalizedAccessToken.length === 0) {
|
|
109
|
-
throw new Error('POSTPLUS_ACCESS_TOKEN cannot be empty.');
|
|
110
|
-
}
|
|
111
|
-
return updateLocalConfig((current) => ({
|
|
112
|
-
...(current ?? {}),
|
|
113
|
-
accessToken: normalizedAccessToken,
|
|
114
|
-
}));
|
|
115
|
-
}
|
|
116
|
-
export async function setLocalRefreshToken(refreshToken) {
|
|
117
|
-
const normalizedRefreshToken = refreshToken.trim();
|
|
118
|
-
if (normalizedRefreshToken.length === 0) {
|
|
119
|
-
throw new Error('POSTPLUS_REFRESH_TOKEN cannot be empty.');
|
|
120
|
-
}
|
|
121
|
-
return updateLocalConfig((current) => ({
|
|
122
|
-
...(current ?? {}),
|
|
123
|
-
refreshToken: normalizedRefreshToken,
|
|
124
|
-
}));
|
|
125
|
-
}
|
|
126
106
|
export async function setLocalApiBaseUrl(apiBaseUrl) {
|
|
127
107
|
const normalizedApiBaseUrl = apiBaseUrl.trim();
|
|
128
108
|
if (normalizedApiBaseUrl.length === 0) {
|
|
@@ -139,10 +119,10 @@ export async function setLocalSession(input) {
|
|
|
139
119
|
const refreshToken = input.refreshToken.trim();
|
|
140
120
|
const apiBaseUrl = input.apiBaseUrl.trim().replace(/\/+$/, '');
|
|
141
121
|
if (accessToken.length === 0) {
|
|
142
|
-
throw new Error('
|
|
122
|
+
throw new Error('PostPlus CLI access token cannot be empty.');
|
|
143
123
|
}
|
|
144
124
|
if (refreshToken.length === 0) {
|
|
145
|
-
throw new Error('
|
|
125
|
+
throw new Error('PostPlus CLI refresh token cannot be empty.');
|
|
146
126
|
}
|
|
147
127
|
if (apiBaseUrl.length === 0) {
|
|
148
128
|
throw new Error('POSTPLUS_API_BASE_URL cannot be empty.');
|
|
@@ -168,14 +148,6 @@ export async function hasLocalConfigFile() {
|
|
|
168
148
|
}
|
|
169
149
|
}
|
|
170
150
|
export async function resolveAccessTokenState() {
|
|
171
|
-
const envValue = process.env.POSTPLUS_ACCESS_TOKEN?.trim();
|
|
172
|
-
if (envValue && envValue.length > 0) {
|
|
173
|
-
return {
|
|
174
|
-
source: 'env',
|
|
175
|
-
present: true,
|
|
176
|
-
value: envValue,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
151
|
const config = await readLocalConfig();
|
|
180
152
|
const configValue = config?.accessToken?.trim();
|
|
181
153
|
if (configValue && configValue.length > 0) {
|
|
@@ -192,14 +164,6 @@ export async function resolveAccessTokenState() {
|
|
|
192
164
|
};
|
|
193
165
|
}
|
|
194
166
|
export async function resolveRefreshTokenState() {
|
|
195
|
-
const envValue = process.env.POSTPLUS_REFRESH_TOKEN?.trim();
|
|
196
|
-
if (envValue && envValue.length > 0) {
|
|
197
|
-
return {
|
|
198
|
-
source: 'env',
|
|
199
|
-
present: true,
|
|
200
|
-
value: envValue,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
167
|
const config = await readLocalConfig();
|
|
204
168
|
const configValue = config?.refreshToken?.trim();
|
|
205
169
|
if (configValue && configValue.length > 0) {
|
|
@@ -216,16 +180,12 @@ export async function resolveRefreshTokenState() {
|
|
|
216
180
|
};
|
|
217
181
|
}
|
|
218
182
|
export async function resolveLocalSessionState() {
|
|
219
|
-
const [accessToken, refreshToken
|
|
183
|
+
const [accessToken, refreshToken] = await Promise.all([
|
|
220
184
|
resolveAccessTokenState(),
|
|
221
185
|
resolveRefreshTokenState(),
|
|
222
|
-
readLocalConfig(),
|
|
223
186
|
]);
|
|
224
187
|
return {
|
|
225
188
|
accessToken,
|
|
226
|
-
expiresAt: typeof config?.sessionExpiresAt === 'number'
|
|
227
|
-
? config.sessionExpiresAt
|
|
228
|
-
: null,
|
|
229
189
|
refreshToken,
|
|
230
190
|
};
|
|
231
191
|
}
|
package/build/skill-catalog.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
export const POSTPLUS_SKILLS_REPO = 'PostPlusAI/postplus-skills';
|
|
2
|
-
export const
|
|
2
|
+
export const POSTPLUS_SKILLS_AGENT_TARGETS = [
|
|
3
|
+
'claude-code',
|
|
4
|
+
'codex',
|
|
5
|
+
'cursor',
|
|
6
|
+
'github-copilot',
|
|
7
|
+
'windsurf',
|
|
8
|
+
'trae',
|
|
9
|
+
'trae-cn',
|
|
10
|
+
];
|
|
11
|
+
const POSTPLUS_SKILLS_AGENT_ARGS = POSTPLUS_SKILLS_AGENT_TARGETS.join(' ');
|
|
12
|
+
export const POSTPLUS_SKILLS_INSTALL_COMMAND = `npx -y skills add PostPlusAI/postplus-skills --full-depth --skill '*' --agent ${POSTPLUS_SKILLS_AGENT_ARGS} --yes`;
|
|
3
13
|
export const POSTPLUS_SKILLS_LIST_COMMAND = 'npx -y skills add PostPlusAI/postplus-skills --list --full-depth';
|
|
4
14
|
const POSTPLUS_SKILLS_INDEX_URL = 'https://raw.githubusercontent.com/PostPlusAI/postplus-skills/main/skills/INDEX.md';
|
|
5
15
|
export async function loadPublicSkillCatalog() {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { POSTPLUS_SKILLS_INSTALL_COMMAND, POSTPLUS_SKILLS_REPO, loadPublicSkillCatalog, } from './skill-catalog.js';
|
|
1
|
+
import { POSTPLUS_SKILLS_AGENT_TARGETS, POSTPLUS_SKILLS_INSTALL_COMMAND, POSTPLUS_SKILLS_REPO, loadPublicSkillCatalog, } from './skill-catalog.js';
|
|
2
2
|
import { runCommand, runInteractiveCommand } from './command-runner.js';
|
|
3
|
-
const SKILLS_AGENTS = ['claude-code', 'codex', 'cursor'];
|
|
4
3
|
const NPX_SKILLS = ['-y', 'skills'];
|
|
5
4
|
export async function runPostPlusSkillUpdate() {
|
|
6
5
|
const catalog = await loadPublicSkillCatalog();
|
|
@@ -93,7 +92,7 @@ export function buildPostPlusSkillUninstallArgs(skillNames) {
|
|
|
93
92
|
'remove',
|
|
94
93
|
...skillNames,
|
|
95
94
|
'--agent',
|
|
96
|
-
...
|
|
95
|
+
...POSTPLUS_SKILLS_AGENT_TARGETS,
|
|
97
96
|
'--yes',
|
|
98
97
|
];
|
|
99
98
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postplus/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
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.",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"build/auth-lifecycle.js",
|
|
10
10
|
"build/auth-login.js",
|
|
11
|
+
"build/auth-session.js",
|
|
11
12
|
"build/auth-validate.js",
|
|
12
13
|
"build/auth.js",
|
|
13
14
|
"build/command-runner.js",
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
"node": ">=20.10.0"
|
|
32
33
|
},
|
|
33
34
|
"bin": {
|
|
34
|
-
"postplus": "
|
|
35
|
+
"postplus": "build/index.js"
|
|
35
36
|
},
|
|
36
37
|
"scripts": {
|
|
37
38
|
"build": "node ./scripts/clean-build.mjs && tsc",
|