sealcode 1.1.1 → 1.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sealcode",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Lock your source code in your own git repo. Stop AI agents, scrapers, and curious eyes from reading what's yours.",
5
5
  "keywords": [
6
6
  "encryption",
package/src/cli-grants.js CHANGED
@@ -299,8 +299,14 @@ async function runGrants({ projectRoot, json = false }) {
299
299
  process.stdout.write(`\n${link.projectName || link.projectId}: ${grants.length} grant(s)\n`);
300
300
  for (const g of grants) {
301
301
  const state = g.state.padEnd(8);
302
- const remaining =
303
- g.state === 'active' ? formatRemaining(g.remainingMs) : g.state;
302
+ let remaining;
303
+ if (g.state === 'active') {
304
+ remaining = formatRemaining(g.remainingMs);
305
+ } else if (g.state === 'paused') {
306
+ remaining = 'paused';
307
+ } else {
308
+ remaining = g.state;
309
+ }
304
310
  const who = g.developerEmail || g.developerLabel || '(no label)';
305
311
  process.stdout.write(
306
312
  ` ${state} ${g.codePrefix.padEnd(12)} ${remaining.padEnd(10)} ${who} id=${g.id}\n`,
@@ -309,6 +315,85 @@ async function runGrants({ projectRoot, json = false }) {
309
315
  process.stdout.write('\n');
310
316
  }
311
317
 
318
+ /**
319
+ * sealcode@1.2.0 — Pause an active access code from the CLI.
320
+ *
321
+ * sealcode pause <grantId> [--reason "<text>"] [--extend-on-resume]
322
+ *
323
+ * Mirrors the dashboard pause modal. Owner must be `sealcode login`ed
324
+ * and be admin (or owner) of the project.
325
+ */
326
+ async function runPause({ grantId, reason, extendOnResume = false }) {
327
+ if (!grantId) throw new Error('Usage: sealcode pause <grantId> [--reason "<text>"] [--extend-on-resume]');
328
+ const body = {};
329
+ if (reason) body.reason = String(reason).slice(0, 500);
330
+ if (extendOnResume) body.extendsExpiry = true;
331
+ try {
332
+ await request(
333
+ 'POST',
334
+ `/api/v1/grants/${encodeURIComponent(grantId)}/pause`,
335
+ { auth: true, body },
336
+ );
337
+ } catch (err) {
338
+ if (err instanceof ApiError && err.status === 409 && err.apiCode === 'wrong_state') {
339
+ ui.fail(err.message || 'Grant is not in a pausable state.');
340
+ process.exitCode = 1;
341
+ return;
342
+ }
343
+ throw err;
344
+ }
345
+ process.stdout.write(`⏸ paused grant ${grantId}\n`);
346
+ if (reason) process.stdout.write(` reason: ${reason}\n`);
347
+ if (extendOnResume) {
348
+ process.stdout.write(` extend-on-resume: expiry will shift forward by the paused duration\n`);
349
+ }
350
+ process.stdout.write(' Any live watcher will re-lock the recipient\'s files within seconds.\n');
351
+ process.stdout.write(` Resume with: ${ui.c.cyan(`sealcode resume ${grantId}`)}\n`);
352
+ }
353
+
354
+ /**
355
+ * sealcode@1.2.0 — Resume a paused access code from the CLI.
356
+ *
357
+ * sealcode resume <grantId>
358
+ *
359
+ * If the grant was paused with --extend-on-resume, the expiresAt shifts
360
+ * forward by the just-elapsed pause duration. The recipient must run
361
+ * `sealcode redeem <code>` again to re-open their working copy.
362
+ */
363
+ async function runResume({ grantId }) {
364
+ if (!grantId) throw new Error('Usage: sealcode resume <grantId>');
365
+ let res;
366
+ try {
367
+ res = await request(
368
+ 'POST',
369
+ `/api/v1/grants/${encodeURIComponent(grantId)}/resume`,
370
+ { auth: true, body: {} },
371
+ );
372
+ } catch (err) {
373
+ if (err instanceof ApiError) {
374
+ if (err.status === 410) {
375
+ ui.fail(err.message || 'Grant expired while paused.');
376
+ ui.hint(' Issue a fresh access code instead: `sealcode share ...`');
377
+ process.exitCode = 1;
378
+ return;
379
+ }
380
+ if (err.status === 409 && err.apiCode === 'wrong_state') {
381
+ ui.fail(err.message || 'Grant is not paused.');
382
+ process.exitCode = 1;
383
+ return;
384
+ }
385
+ }
386
+ throw err;
387
+ }
388
+ process.stdout.write(`▶ resumed grant ${grantId}\n`);
389
+ if (res.newExpiresAt) process.stdout.write(` expires: ${res.newExpiresAt}\n`);
390
+ if (res.extendedExpiry) {
391
+ const mins = Math.round((res.pausedDurationMs || 0) / 60000);
392
+ process.stdout.write(` expiry shifted forward by ~${mins}m (paused duration)\n`);
393
+ }
394
+ process.stdout.write(' Recipient must re-redeem the code to re-open the vault.\n');
395
+ }
396
+
312
397
  async function runRevoke({ grantId }) {
313
398
  if (!grantId) throw new Error('Usage: sealcode revoke <grantId>');
314
399
  const res = await request(
@@ -574,4 +659,12 @@ async function runLockdown({ projectRoot, confirm = true } = {}) {
574
659
  }
575
660
  }
576
661
 
577
- module.exports = { runShare, runGrants, runRevoke, runRedeem, runLockdown };
662
+ module.exports = {
663
+ runShare,
664
+ runGrants,
665
+ runRevoke,
666
+ runRedeem,
667
+ runLockdown,
668
+ runPause,
669
+ runResume,
670
+ };
package/src/cli-watch.js CHANGED
@@ -780,7 +780,14 @@ async function finalLock(projectRoot, config, code, reason, json, daemon) {
780
780
  process.stdout.write(JSON.stringify({ type: 'locked', count: res.count, reason: label }) + '\n');
781
781
  } else {
782
782
  ui.ok(`[${ts()}] re-locked ${ui.c.bold(res.count)} files into ${ui.c.cyan(config.lockedDir + '/')} — session cleared`);
783
- ui.hint(' Your local plaintext has been wiped. Ask the owner for a fresh code if you still need access.');
783
+ // sealcode@1.2.0 be specific about the "paused" case so the
784
+ // recipient knows it's reversible and what their next step is.
785
+ if (label === 'paused') {
786
+ ui.hint(' This access code was PAUSED by the owner — not revoked.');
787
+ ui.hint(` Wait for the owner to resume, then run: ${ui.c.cyan(`sealcode redeem ${code}`)}`);
788
+ } else {
789
+ ui.hint(' Your local plaintext has been wiped. Ask the owner for a fresh code if you still need access.');
790
+ }
784
791
  }
785
792
  process.exit(0);
786
793
  }
package/src/cli.js CHANGED
@@ -33,7 +33,14 @@ const { installHook, uninstallHook } = require('./hooks');
33
33
  const { exportBundle, importBundle } = require('./bundle');
34
34
  const { runLogin, runSignout, runWhoami } = require('./cli-auth');
35
35
  const { runLink, runUnlink, runLinkInfo } = require('./cli-link');
36
- const { runShare, runGrants, runRevoke, runRedeem } = require('./cli-grants');
36
+ const {
37
+ runShare,
38
+ runGrants,
39
+ runRevoke,
40
+ runRedeem,
41
+ runPause,
42
+ runResume,
43
+ } = require('./cli-grants');
37
44
  const {
38
45
  runCiCreate,
39
46
  runCiList,
@@ -748,6 +755,38 @@ function build() {
748
755
  }
749
756
  });
750
757
 
758
+ // -------- pause (sealcode@1.2.0) --------
759
+ program
760
+ .command('pause')
761
+ .argument('<grantId>', 'grant ID to pause (see `sealcode grants`)')
762
+ .description('Pause an active access code. Reversible: any live watcher re-locks immediately, but you can resume later.')
763
+ .option('--reason <text>', 'optional note visible to project admins (≤500 chars)')
764
+ .option('--extend-on-resume', 'on resume, shift expiry forward by the paused duration (default: clock keeps ticking)', false)
765
+ .action(async (grantId, opts) => {
766
+ try {
767
+ await runPause({
768
+ grantId,
769
+ reason: opts.reason,
770
+ extendOnResume: !!opts.extendOnResume,
771
+ });
772
+ } catch (err) {
773
+ process.exitCode = reportError(err);
774
+ }
775
+ });
776
+
777
+ // -------- resume (sealcode@1.2.0) --------
778
+ program
779
+ .command('resume')
780
+ .argument('<grantId>', 'grant ID to resume (see `sealcode grants`)')
781
+ .description('Resume a paused access code. The recipient must `sealcode redeem <code>` again to re-open their working copy.')
782
+ .action(async (grantId) => {
783
+ try {
784
+ await runResume({ grantId });
785
+ } catch (err) {
786
+ process.exitCode = reportError(err);
787
+ }
788
+ });
789
+
751
790
  // -------- lockdown (sealcode@1.1.0) --------
752
791
  program
753
792
  .command('lockdown')