@postplus/cli 0.1.20 → 0.1.21

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.
@@ -1,7 +1,8 @@
1
1
  import { refreshRemoteAuthSession } from './auth-session.js';
2
2
  import { clearAuthState, generateAuthStatusReport } from './auth.js';
3
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, } from './client-compatibility.js';
3
4
  import { requireHostedBaseUrl } from './hosted-release.js';
4
- import { resolveAccessTokenState, resolveRefreshTokenState, } from './local-state.js';
5
+ import { resolveCliSessionTokenState } from './local-state.js';
5
6
  export async function refreshRemoteAuth() {
6
7
  const refreshed = await refreshRemoteAuthSession();
7
8
  return {
@@ -25,31 +26,31 @@ export function formatAuthRefreshReport(report) {
25
26
  ].join('\n');
26
27
  }
27
28
  export async function revokeRemoteAuth() {
28
- const [apiBaseUrl, accessTokenState, refreshTokenState] = await Promise.all([
29
+ const [apiBaseUrl, cliSessionTokenState] = await Promise.all([
29
30
  requireHostedBaseUrl(),
30
- resolveAccessTokenState(),
31
- resolveRefreshTokenState(),
31
+ resolveCliSessionTokenState(),
32
32
  ]);
33
- if (!accessTokenState.present || !accessTokenState.value) {
34
- throw new Error('Run `postplus auth login` before revoking PostPlus auth.');
35
- }
36
- if (!refreshTokenState.present || !refreshTokenState.value) {
33
+ if (!cliSessionTokenState.present || !cliSessionTokenState.value) {
37
34
  throw new Error('Run `postplus auth login` before revoking PostPlus auth.');
38
35
  }
36
+ const compatibilityHeaders = await buildPostPlusClientCompatibilityHeaders();
39
37
  const response = await fetch(`${apiBaseUrl}/api/postplus-cli/auth/revoke`, {
40
38
  method: 'POST',
41
39
  headers: {
42
40
  accept: 'application/json',
43
- authorization: `Bearer ${accessTokenState.value}`,
41
+ ...compatibilityHeaders,
42
+ authorization: `Bearer ${cliSessionTokenState.value}`,
44
43
  'content-type': 'application/json',
45
44
  },
46
- body: JSON.stringify({
47
- refreshToken: refreshTokenState.value,
48
- }),
45
+ body: JSON.stringify({}),
49
46
  signal: AbortSignal.timeout(15000),
50
47
  });
51
48
  const payload = (await response.json());
52
49
  if (!response.ok) {
50
+ if ('code' in payload &&
51
+ payload.code === 'postplus_client_upgrade_required') {
52
+ throw new Error(formatPostPlusClientUpgradeError(payload));
53
+ }
53
54
  throw new Error('error' in payload && typeof payload.error === 'string'
54
55
  ? payload.error
55
56
  : 'Failed to revoke remote PostPlus auth.');
@@ -1,3 +1,4 @@
1
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, writeCurrentCliVersionToLocalConfig, } from './client-compatibility.js';
1
2
  import { requireHostedBaseUrl } from './hosted-release.js';
2
3
  import { setLocalSession } from './local-state.js';
3
4
  export const CLI_AUTH_LOGIN_TIMEOUT_MS = 30 * 60 * 1000;
@@ -23,18 +24,18 @@ export async function loginWithCloudHandoff() {
23
24
  requestId: started.requestId,
24
25
  });
25
26
  const validated = await validateCliSession({
26
- accessToken: handoffPayload.accessToken,
27
27
  apiBaseUrl: baseUrl,
28
+ cliSessionToken: handoffPayload.cliSessionToken,
28
29
  });
29
30
  await setLocalSession({
30
- accessToken: handoffPayload.accessToken,
31
31
  accountId: validated.accountId,
32
32
  apiBaseUrl: baseUrl,
33
- refreshToken: handoffPayload.refreshToken,
33
+ cliSessionToken: handoffPayload.cliSessionToken,
34
34
  sessionExpiresAt: validated.sessionExpiresAt ?? handoffPayload.sessionExpiresAt ?? null,
35
35
  userEmail: validated.userEmail,
36
36
  userId: validated.userId,
37
37
  });
38
+ await writeCurrentCliVersionToLocalConfig();
38
39
  return {
39
40
  accountId: validated.accountId,
40
41
  apiBaseUrl: baseUrl,
@@ -44,10 +45,12 @@ export async function loginWithCloudHandoff() {
44
45
  };
45
46
  }
46
47
  export async function startCloudAuthLogin(apiBaseUrl) {
48
+ const compatibilityHeaders = await buildPostPlusClientCompatibilityHeaders();
47
49
  const response = await fetch(`${apiBaseUrl}/api/postplus-cli/auth/login/start`, {
48
50
  method: 'POST',
49
51
  headers: {
50
52
  accept: 'application/json',
53
+ ...compatibilityHeaders,
51
54
  },
52
55
  signal: AbortSignal.timeout(15000),
53
56
  });
@@ -76,10 +79,12 @@ async function waitForCloudAuthLogin(input) {
76
79
  throw new Error('Timed out waiting for the cloud sign-in handoff.');
77
80
  }
78
81
  export async function pollCloudAuthLogin(input) {
82
+ const compatibilityHeaders = await buildPostPlusClientCompatibilityHeaders();
79
83
  const response = await fetch(`${input.apiBaseUrl}/api/postplus-cli/auth/login/poll`, {
80
84
  method: 'POST',
81
85
  headers: {
82
86
  accept: 'application/json',
87
+ ...compatibilityHeaders,
83
88
  'content-type': 'application/json',
84
89
  },
85
90
  body: JSON.stringify({
@@ -101,11 +106,13 @@ export async function pollCloudAuthLogin(input) {
101
106
  throw new Error('PostPlus CLI sign-in poll returned incomplete data.');
102
107
  }
103
108
  export async function validateCliSession(input) {
109
+ const compatibilityHeaders = await buildPostPlusClientCompatibilityHeaders();
104
110
  const response = await fetch(`${input.apiBaseUrl}/api/postplus-cli/auth/whoami`, {
105
111
  method: 'GET',
106
112
  headers: {
107
113
  accept: 'application/json',
108
- authorization: `Bearer ${input.accessToken}`,
114
+ ...compatibilityHeaders,
115
+ authorization: `Bearer ${input.cliSessionToken}`,
109
116
  },
110
117
  signal: AbortSignal.timeout(15000),
111
118
  });
@@ -123,6 +130,9 @@ export function formatCliSessionAuthError(payload) {
123
130
  'Once the environment is ready, the CLI will automatically obtain and store its session.',
124
131
  ].join(' ');
125
132
  }
133
+ if (payload.code === 'postplus_client_upgrade_required') {
134
+ return formatPostPlusClientUpgradeError(payload);
135
+ }
126
136
  if (typeof payload.error === 'string' && payload.error.trim().length > 0) {
127
137
  return payload.error;
128
138
  }
@@ -140,8 +150,7 @@ function isCliAuthLoginStartSuccessPayload(payload) {
140
150
  function isCliAuthLoginCompletedPayload(payload) {
141
151
  return ('status' in payload &&
142
152
  payload.status === 'completed' &&
143
- typeof payload.accessToken === 'string' &&
144
- typeof payload.refreshToken === 'string' &&
153
+ typeof payload.cliSessionToken === 'string' &&
145
154
  typeof payload.accountId === 'string' &&
146
155
  typeof payload.userId === 'string');
147
156
  }
@@ -149,6 +158,9 @@ function isCliAuthLoginPendingPayload(payload) {
149
158
  return 'status' in payload && payload.status === 'pending';
150
159
  }
151
160
  function formatRemoteAuthLoginError(payload) {
161
+ if ('code' in payload && payload.code === 'postplus_client_upgrade_required') {
162
+ return formatPostPlusClientUpgradeError(payload);
163
+ }
152
164
  return 'error' in payload &&
153
165
  typeof payload.error === 'string' &&
154
166
  payload.error.trim().length > 0
@@ -1,137 +1,89 @@
1
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, writeCurrentCliVersionToLocalConfig, } from './client-compatibility.js';
1
2
  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;
3
+ import { resolveCliSessionTokenState, setLocalSession, } from './local-state.js';
4
4
  export async function resolveFreshRemoteAuth(options = {}) {
5
- const [apiBaseUrl, accessTokenState, refreshTokenState, config] = await Promise.all([
5
+ const [apiBaseUrl, cliSessionTokenState] = await Promise.all([
6
6
  requireHostedBaseUrl(),
7
- resolveAccessTokenState(),
8
- resolveRefreshTokenState(),
9
- readLocalConfig(),
7
+ resolveCliSessionTokenState(),
10
8
  ]);
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
- };
9
+ if (!cliSessionTokenState.present || !cliSessionTokenState.value) {
10
+ throw new Error('Run `postplus auth login` before using PostPlus auth.');
21
11
  }
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
- }
12
+ if (options.forceRefresh === true) {
13
+ const refreshed = await refreshRemoteAuthSession({
14
+ apiBaseUrl,
15
+ cliSessionToken: cliSessionTokenState.value,
16
+ });
39
17
  return {
40
- accessToken: existingAccessToken,
41
18
  apiBaseUrl,
42
- refreshed: false,
19
+ cliSessionToken: refreshed.cliSessionToken,
20
+ refreshed: true,
43
21
  source: 'config',
44
22
  };
45
23
  }
46
- const refreshed = await refreshRemoteAuthSession({
47
- accessToken: accessTokenState.value,
48
- apiBaseUrl,
49
- refreshToken: refreshTokenState.value,
50
- });
51
24
  return {
52
- accessToken: refreshed.accessToken,
53
25
  apiBaseUrl,
54
- refreshed: true,
26
+ cliSessionToken: cliSessionTokenState.value,
27
+ refreshed: false,
55
28
  source: 'config',
56
29
  };
57
30
  }
58
31
  export async function refreshRemoteAuthSession(input) {
59
- const [apiBaseUrl, accessTokenState, refreshTokenState] = await Promise.all([
32
+ const [apiBaseUrl, cliSessionTokenState] = await Promise.all([
60
33
  input?.apiBaseUrl ?? requireHostedBaseUrl(),
61
- input?.accessToken === undefined ? resolveAccessTokenState() : null,
62
- input?.refreshToken === undefined ? resolveRefreshTokenState() : null,
34
+ input?.cliSessionToken === undefined ? resolveCliSessionTokenState() : null,
63
35
  ]);
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) {
36
+ const cliSessionToken = input?.cliSessionToken === undefined
37
+ ? cliSessionTokenState?.value
38
+ : input.cliSessionToken;
39
+ if (!cliSessionToken) {
71
40
  throw new Error('Run `postplus auth login` before refreshing PostPlus auth.');
72
41
  }
42
+ const compatibilityHeaders = await buildPostPlusClientCompatibilityHeaders();
73
43
  const response = await fetch(`${apiBaseUrl}/api/postplus-cli/auth/refresh`, {
74
44
  method: 'POST',
75
45
  headers: {
76
46
  accept: 'application/json',
77
- ...(accessToken ? { authorization: `Bearer ${accessToken}` } : {}),
47
+ ...compatibilityHeaders,
48
+ authorization: `Bearer ${cliSessionToken}`,
78
49
  'content-type': 'application/json',
79
50
  },
80
- body: JSON.stringify({
81
- refreshToken,
82
- }),
51
+ body: JSON.stringify({}),
83
52
  signal: AbortSignal.timeout(15000),
84
53
  });
85
54
  const payload = (await response.json());
86
55
  if (!response.ok) {
56
+ if ('code' in payload &&
57
+ payload.code === 'postplus_client_upgrade_required') {
58
+ throw new Error(formatPostPlusClientUpgradeError(payload));
59
+ }
87
60
  throw new Error('error' in payload && typeof payload.error === 'string'
88
61
  ? payload.error
89
62
  : 'Failed to refresh remote PostPlus auth.');
90
63
  }
91
64
  if (!isRemoteAuthRefreshSuccessPayload(payload)) {
92
- throw new Error('PostPlus auth refresh returned incomplete session tokens.');
65
+ throw new Error('PostPlus auth refresh returned incomplete session data.');
93
66
  }
94
67
  await setLocalSession({
95
- accessToken: payload.accessToken,
96
68
  accountId: payload.accountId,
97
69
  apiBaseUrl,
98
- refreshToken: payload.refreshToken,
70
+ cliSessionToken: payload.cliSessionToken,
99
71
  sessionExpiresAt: payload.sessionExpiresAt,
100
72
  userEmail: payload.userEmail,
101
73
  userId: payload.userId,
102
74
  });
75
+ await writeCurrentCliVersionToLocalConfig();
103
76
  return {
104
77
  ...payload,
105
78
  apiBaseUrl,
106
79
  };
107
80
  }
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
81
  function isRemoteAuthRefreshSuccessPayload(payload) {
129
82
  return (typeof payload === 'object' &&
130
83
  payload !== null &&
131
- typeof payload.accessToken === 'string' &&
132
- payload.accessToken.trim().length > 0 &&
133
- typeof payload.refreshToken === 'string' &&
134
- payload.refreshToken.trim().length > 0 &&
84
+ typeof payload.cliSessionToken ===
85
+ 'string' &&
86
+ payload.cliSessionToken.trim().length > 0 &&
135
87
  typeof payload.accountId === 'string' &&
136
88
  typeof payload.userId === 'string');
137
89
  }
@@ -1,4 +1,5 @@
1
1
  import { resolveFreshRemoteAuth } from './auth-session.js';
2
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, } from './client-compatibility.js';
2
3
  export async function validateRemoteAuth() {
3
4
  let auth = await resolveFreshRemoteAuth();
4
5
  let response = await fetchWhoami(auth);
@@ -10,6 +11,10 @@ export async function validateRemoteAuth() {
10
11
  }
11
12
  const payload = (await response.json());
12
13
  if (!response.ok) {
14
+ if ('code' in payload &&
15
+ payload.code === 'postplus_client_upgrade_required') {
16
+ throw new Error(formatPostPlusClientUpgradeError(payload));
17
+ }
13
18
  throw new Error('error' in payload && typeof payload.error === 'string'
14
19
  ? payload.error
15
20
  : 'Failed to validate remote PostPlus auth.');
@@ -36,12 +41,14 @@ export function formatAuthValidateReport(report) {
36
41
  `Subscription: ${report.subscriptionStatus ?? 'unknown'}`,
37
42
  ].join('\n');
38
43
  }
39
- function fetchWhoami(input) {
44
+ async function fetchWhoami(input) {
45
+ const compatibilityHeaders = await buildPostPlusClientCompatibilityHeaders();
40
46
  return fetch(`${input.apiBaseUrl}/api/postplus-cli/auth/whoami`, {
41
47
  method: 'GET',
42
48
  headers: {
43
49
  accept: 'application/json',
44
- authorization: `Bearer ${input.accessToken}`,
50
+ ...compatibilityHeaders,
51
+ authorization: `Bearer ${input.cliSessionToken}`,
45
52
  },
46
53
  signal: AbortSignal.timeout(15000),
47
54
  });
package/build/auth.js CHANGED
@@ -7,13 +7,11 @@ export async function generateAuthStatusReport() {
7
7
  readLocalConfig(),
8
8
  ]);
9
9
  return {
10
- ok: sessionState.accessToken.present &&
11
- sessionState.refreshToken.present &&
12
- apiBaseUrlState.present,
13
- accessToken: {
14
- source: sessionState.accessToken.source,
15
- present: sessionState.accessToken.present,
16
- maskedValue: maskSecret(sessionState.accessToken.value),
10
+ ok: sessionState.cliSessionToken.present && apiBaseUrlState.present,
11
+ cliSessionToken: {
12
+ source: sessionState.cliSessionToken.source,
13
+ present: sessionState.cliSessionToken.present,
14
+ maskedValue: maskSecret(sessionState.cliSessionToken.value),
17
15
  },
18
16
  apiBaseUrl: {
19
17
  source: apiBaseUrlState.source,
@@ -24,29 +22,21 @@ export async function generateAuthStatusReport() {
24
22
  path: getPostPlusConfigPath(),
25
23
  exists: configExists,
26
24
  accountId: config?.accountId?.trim() || null,
25
+ sessionExpiresAt: typeof config?.sessionExpiresAt === 'number'
26
+ ? config.sessionExpiresAt
27
+ : null,
27
28
  userEmail: typeof config?.userEmail === 'string' ? config.userEmail.trim() : null,
28
29
  userId: config?.userId?.trim() || null,
29
30
  },
30
- refreshToken: {
31
- source: sessionState.refreshToken.source,
32
- present: sessionState.refreshToken.present,
33
- maskedValue: maskSecret(sessionState.refreshToken.value),
34
- },
35
31
  };
36
32
  }
37
33
  export function formatAuthStatusReport(report) {
38
34
  const lines = ['PostPlus CLI auth status', ''];
39
- lines.push(report.accessToken.present
40
- ? `[PASS] Access token: present (${report.accessToken.source})`
41
- : '[FAIL] Access token: missing');
42
- lines.push(report.accessToken.maskedValue
43
- ? ` Value: ${report.accessToken.maskedValue}`
44
- : ' Value: not configured');
45
- lines.push(report.refreshToken.present
46
- ? `[PASS] Refresh token: present (${report.refreshToken.source})`
47
- : '[FAIL] Refresh token: missing');
48
- lines.push(report.refreshToken.maskedValue
49
- ? ` Value: ${report.refreshToken.maskedValue}`
35
+ lines.push(report.cliSessionToken.present
36
+ ? `[PASS] CLI session token: present (${report.cliSessionToken.source})`
37
+ : '[FAIL] CLI session token: missing');
38
+ lines.push(report.cliSessionToken.maskedValue
39
+ ? ` Value: ${report.cliSessionToken.maskedValue}`
50
40
  : ' Value: not configured');
51
41
  lines.push(report.apiBaseUrl.present
52
42
  ? `[PASS] PostPlus Cloud: configured (${report.apiBaseUrl.source})`
@@ -59,6 +49,9 @@ export function formatAuthStatusReport(report) {
59
49
  : `[PASS] local config path: ${report.config.path}`);
60
50
  lines.push(` Account: ${report.config.accountId ?? 'not bound'}`);
61
51
  lines.push(` User: ${report.config.userEmail ?? report.config.userId ?? 'not bound'}`);
52
+ lines.push(` Expires: ${report.config.sessionExpiresAt
53
+ ? new Date(report.config.sessionExpiresAt * 1000).toISOString()
54
+ : 'unknown'}`);
62
55
  lines.push('', report.ok ? 'Auth status OK.' : 'Auth status incomplete.');
63
56
  return lines.join('\n');
64
57
  }
package/build/doctor.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { resolveFreshRemoteAuth, } from './auth-session.js';
2
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, } from './client-compatibility.js';
2
3
  import { resolveHostedBaseUrl } from './hosted-release.js';
3
4
  import { formatLocalDependencyReport, generateLocalDependencyReport, } from './local-dependencies.js';
4
5
  function createPass(id, label, detail, severity = 'required') {
@@ -184,15 +185,20 @@ function readReadinessCheckFailureLabel(value) {
184
185
  : 'unknown check';
185
186
  }
186
187
  function readErrorMessage(payload, fallback) {
188
+ if (payload.code === 'postplus_client_upgrade_required') {
189
+ return formatPostPlusClientUpgradeError(payload);
190
+ }
187
191
  return typeof payload.error === 'string' && payload.error.trim().length > 0
188
192
  ? payload.error
189
193
  : fallback;
190
194
  }
191
- function requestWithAuth(input, path) {
195
+ async function requestWithAuth(input, path) {
196
+ const compatibilityHeaders = await buildPostPlusClientCompatibilityHeaders();
192
197
  return fetch(`${input.apiBaseUrl}${path}`, {
193
198
  headers: {
194
199
  accept: 'application/json',
195
- authorization: `Bearer ${input.accessToken}`,
200
+ ...compatibilityHeaders,
201
+ authorization: `Bearer ${input.cliSessionToken}`,
196
202
  },
197
203
  signal: AbortSignal.timeout(15000),
198
204
  });
package/build/index.js CHANGED
@@ -8,7 +8,8 @@ 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';
10
10
  import { formatStatusReport, generateStatusReport } from './status.js';
11
- import { readCurrentCliVersion, refreshUpdateCheckCache, } from './update-check.js';
11
+ import { readCurrentCliVersion, } from './client-compatibility.js';
12
+ import { refreshUpdateCheckCache } from './update-check.js';
12
13
  function printAuthHelp() {
13
14
  process.stdout.write(`PostPlus CLI — auth commands
14
15
 
@@ -95,6 +95,7 @@ export async function clearLocalAuthState() {
95
95
  delete next.accessToken;
96
96
  delete next.accountId;
97
97
  delete next.apiKey;
98
+ delete next.cliSessionToken;
98
99
  delete next.machineId;
99
100
  delete next.refreshToken;
100
101
  delete next.sessionExpiresAt;
@@ -150,24 +151,19 @@ export async function setLocalApiBaseUrl(apiBaseUrl) {
150
151
  }));
151
152
  }
152
153
  export async function setLocalSession(input) {
153
- const accessToken = input.accessToken.trim();
154
- const refreshToken = input.refreshToken.trim();
154
+ const cliSessionToken = input.cliSessionToken.trim();
155
155
  const apiBaseUrl = input.apiBaseUrl.trim().replace(/\/+$/, '');
156
- if (accessToken.length === 0) {
157
- throw new Error('PostPlus CLI access token cannot be empty.');
158
- }
159
- if (refreshToken.length === 0) {
160
- throw new Error('PostPlus CLI refresh token cannot be empty.');
156
+ if (cliSessionToken.length === 0) {
157
+ throw new Error('PostPlus CLI session token cannot be empty.');
161
158
  }
162
159
  if (apiBaseUrl.length === 0) {
163
160
  throw new Error('POSTPLUS_API_BASE_URL cannot be empty.');
164
161
  }
165
162
  return updateLocalConfig((current) => ({
166
163
  ...omitLegacyAuthFields(current),
167
- accessToken,
168
164
  accountId: input.accountId,
169
165
  apiBaseUrl,
170
- refreshToken,
166
+ cliSessionToken,
171
167
  sessionExpiresAt: input.sessionExpiresAt,
172
168
  userEmail: input.userEmail,
173
169
  userId: input.userId,
@@ -182,6 +178,22 @@ export async function hasLocalConfigFile() {
182
178
  return false;
183
179
  }
184
180
  }
181
+ export async function resolveCliSessionTokenState() {
182
+ const config = await readLocalConfig();
183
+ const configValue = config?.cliSessionToken?.trim();
184
+ if (configValue && configValue.length > 0) {
185
+ return {
186
+ source: 'config',
187
+ present: true,
188
+ value: configValue,
189
+ };
190
+ }
191
+ return {
192
+ source: 'missing',
193
+ present: false,
194
+ value: null,
195
+ };
196
+ }
185
197
  export async function resolveAccessTokenState() {
186
198
  const config = await readLocalConfig();
187
199
  const configValue = config?.accessToken?.trim();
@@ -215,13 +227,9 @@ export async function resolveRefreshTokenState() {
215
227
  };
216
228
  }
217
229
  export async function resolveLocalSessionState() {
218
- const [accessToken, refreshToken] = await Promise.all([
219
- resolveAccessTokenState(),
220
- resolveRefreshTokenState(),
221
- ]);
230
+ const cliSessionToken = await resolveCliSessionTokenState();
222
231
  return {
223
- accessToken,
224
- refreshToken,
232
+ cliSessionToken,
225
233
  };
226
234
  }
227
235
  export async function resolveApiBaseUrlState() {
@@ -258,7 +266,7 @@ export function maskSecret(value) {
258
266
  return `${value.slice(0, 4)}…${value.slice(-4)}`;
259
267
  }
260
268
  function omitLegacyAuthFields(current) {
261
- const { apiKey: _apiKey, machineId: _machineId, ...rest } = (current ?? {});
269
+ const { apiKey: _apiKey, accessToken: _accessToken, machineId: _machineId, refreshToken: _refreshToken, ...rest } = (current ?? {});
262
270
  return rest;
263
271
  }
264
272
  function normalizeSkillNames(values) {
@@ -1,4 +1,5 @@
1
1
  import { runCommand, runInteractiveCommand } from './command-runner.js';
2
+ import { writeCurrentCliVersionToLocalConfig } from './client-compatibility.js';
2
3
  import { clearManagedSkillBaseline, readManagedSkillBaseline, writeManagedSkillBaseline, } from './local-state.js';
3
4
  import { POSTPLUS_SKILLS_AGENT_TARGETS, POSTPLUS_SKILLS_INSTALL_COMMAND, POSTPLUS_SKILLS_REPO, loadPublicSkillCatalog, } from './skill-catalog.js';
4
5
  const NPX_SKILLS = ['-y', 'skills'];
@@ -26,6 +27,7 @@ export async function runPostPlusSkillUpdate(dependencies = {
26
27
  revision: catalog.revision,
27
28
  skillNames,
28
29
  });
30
+ await writeCurrentCliVersionToLocalConfig();
29
31
  return 0;
30
32
  }
31
33
  export async function runPostPlusSkillUninstall(dependencies = {
package/build/status.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { formatAuthStatusReport, generateAuthStatusReport, } from './auth.js';
2
+ import { writeCurrentCliVersionToLocalConfig } from './client-compatibility.js';
2
3
  import { formatDoctorReport, generateDoctorReport, } from './doctor.js';
3
4
  import { formatSkillInstallStatusReport, generateSkillInstallStatusReport, } from './skill-management.js';
4
5
  import { formatUpdateStatusReport, generateUpdateStatusReport, } from './update-check.js';
@@ -6,6 +7,7 @@ export async function generateStatusReport() {
6
7
  return generateStatusReportWithDependencies();
7
8
  }
8
9
  export async function generateStatusReportWithDependencies(dependencies = {}) {
10
+ await writeCurrentCliVersionToLocalConfig();
9
11
  const generateAuthStatus = dependencies.generateAuthStatus ?? generateAuthStatusReport;
10
12
  const generateDoctor = dependencies.generateDoctor ?? generateDoctorReport;
11
13
  const generateSkillStatus = dependencies.generateSkillStatus ?? generateSkillInstallStatusReport;
@@ -1,5 +1,6 @@
1
1
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
+ import { readCurrentCliVersion } from './client-compatibility.js';
3
4
  import { getPostPlusConfigDir, readManagedSkillBaseline, } from './local-state.js';
4
5
  import { POSTPLUS_SKILLS_REPO, loadPublicSkillCatalog, } from './skill-catalog.js';
5
6
  const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
@@ -125,15 +126,6 @@ function buildUpdateReport(input) {
125
126
  warning: null,
126
127
  };
127
128
  }
128
- export async function readCurrentCliVersion() {
129
- const packageJsonPath = new URL('../package.json', import.meta.url);
130
- const raw = await readFile(packageJsonPath, 'utf8');
131
- const parsed = JSON.parse(raw);
132
- if (typeof parsed.version !== 'string' || !parsed.version.trim()) {
133
- throw new Error('Could not read the current PostPlus CLI version.');
134
- }
135
- return parsed.version.trim();
136
- }
137
129
  async function fetchLatestCliVersion(fetchFn) {
138
130
  const response = await fetchFn(NPM_LATEST_URL, {
139
131
  headers: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postplus/cli",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
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.",