@shoplifter/cli 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Shoplifter CLI
2
2
 
3
- Requires Node 24. Install with `npm install --global @shoplifter/cli@0.1.1`.
3
+ Requires Node 24. Install with `npm install --global @shoplifter/cli@0.1.2`.
4
4
  It continues using SDK and contract `0.1.0`; CLI patches do not upgrade runtime inputs.
5
5
 
6
6
  ## Authoring
@@ -44,6 +44,9 @@ Only channels accessible to your dashboard account can be selected; publishing m
44
44
  be enabled for the channel. Switching channels clears the reviewed request.
45
45
  One-hour credentials
46
46
  are stored with private permissions outside the project. Use `shoplifter logout` to revoke.
47
+ CLI `0.1.2` tolerates up to five minutes of server clock lead when validating a
48
+ new one-hour credential; server-side token expiry remains one hour. This fixes
49
+ approved logins rejected by `0.1.1` when server and developer clocks differ slightly.
47
50
  `shoplifter publication upload <channel-code>` uploads source; `stage`, `preview`,
48
51
  `build`, `activate`, `publish`, `status`, `logs`, `retry`, `retry-deployment` and
49
52
  `rollback` operate on channel-scoped revisions. `preview-link` requests protected
package/dist/auth.js CHANGED
@@ -14,6 +14,8 @@ export function apiOrigin() {
14
14
  }
15
15
  const directory = () => resolve(process.env.SHOPLIFTER_CONFIG_DIRECTORY ?? resolve(homedir(), '.config/shoplifter'));
16
16
  const filename = () => resolve(directory(), createHash('sha256').update(apiOrigin().origin).digest('hex') + '.json');
17
+ // The server enforces token lifetime. Allow bounded clock skew when checking its expiry locally.
18
+ const credentialClockSkewMs = 5 * 60 * 1000;
17
19
  export async function credentials(required = true) {
18
20
  try {
19
21
  if (process.platform !== 'win32' && (await stat(filename())).mode & 0o077)
@@ -63,10 +65,13 @@ export async function login(channelCode) {
63
65
  continue;
64
66
  if (result.status !== 'approved')
65
67
  throw new Error('CLI authorization denied or expired');
66
- if (!/^slw_[A-Za-z0-9_-]{43}$/.test(result.token) || !/^\d+$/.test(result.channelId)
67
- || !Number.isFinite(Date.parse(result.expiresAt)) || Date.parse(result.expiresAt) <= Date.now()
68
- || Date.parse(result.expiresAt) > Date.now() + 3600000)
69
- throw new Error('Invalid CLI credential response');
68
+ if (!/^slw_[A-Za-z0-9_-]{43}$/.test(result.token) || !/^\d+$/.test(result.channelId))
69
+ throw new Error('Invalid CLI credential response (session format)');
70
+ const expiresAt = Date.parse(result.expiresAt), now = Date.now();
71
+ if (!Number.isFinite(expiresAt) || expiresAt <= now)
72
+ throw new Error('Invalid CLI credential response (invalid or expired session)');
73
+ if (expiresAt > now + 3600000 + credentialClockSkewMs)
74
+ throw new Error('Invalid CLI credential response (expiry exceeds one hour plus clock tolerance; check your system clock)');
70
75
  const approvedCode = await sessionChannel(result.token);
71
76
  if (channelCode && approvedCode !== channelCode) {
72
77
  await response(await fetch(new URL('/shoplifter-cli/logout', origin), { method: 'POST', headers: { authorization: 'Bearer ' + result.token }, signal: AbortSignal.timeout(15000) }));
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
13
13
  };
14
14
  import { program, command, requiredArg, optionalArg, description, version } from 'commander-ts';
15
15
  import { spawn } from 'node:child_process';
16
+ import { readFileSync } from 'node:fs';
16
17
  import { initialize, editProject, scaffold } from './author.js';
17
18
  import { generate, validateImages } from './files.js';
18
19
  import { publication, previewUpload } from './publication.js';
@@ -207,7 +208,7 @@ __decorate([
207
208
  ], Shoplifter.prototype, "publication", null);
208
209
  Shoplifter = __decorate([
209
210
  program(),
210
- version('0.1.1'),
211
+ version(JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version),
211
212
  description('Author and publish independent Shoplifter presentations')
212
213
  ], Shoplifter);
213
214
  if (Number(process.versions.node.split('.')[0]) !== 24)
@@ -11,7 +11,7 @@
11
11
  },
12
12
  {
13
13
  "path": "auth.js",
14
- "sha256": "282c5df5cc768f33e2f96052fb21758f74ca3875bc5c9c582c439a490ceed15e"
14
+ "sha256": "848d11a0471e0e82cfd759b82b5a927517bcb6ff89b47e12f4b588995bd32077"
15
15
  },
16
16
  {
17
17
  "path": "author.d.ts",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  {
37
37
  "path": "index.js",
38
- "sha256": "4b85f13c321650f3a35e9cdee6df2eb8608d815750886b99a5850b224441e3c4"
38
+ "sha256": "d8a1ef1be4575a581f63ca1c51f71327b91728dfda1ddf77ece556b8f7847899"
39
39
  },
40
40
  {
41
41
  "path": "publication.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shoplifter/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "shoplifterRuntimeVersion": "0.1.0",
5
5
  "private": false,
6
6
  "license": "Apache-2.0",