@thejoseki/clawform 2.3.0 → 2.3.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 +141 -120
- package/bin/clawform.js +161 -151
- package/package.json +65 -65
- package/src/aws/catalog.js +116 -116
- package/src/aws/changeset.js +231 -231
- package/src/aws/drift.js +59 -59
- package/src/aws/exports.js +213 -213
- package/src/aws/inventory.js +156 -156
- package/src/commands/activate.js +34 -2
- package/src/commands/ci-init.js +393 -212
- package/src/commands/cost-report.js +163 -163
- package/src/commands/deactivate.js +9 -2
- package/src/commands/deploy.js +413 -413
- package/src/commands/diff.js +39 -39
- package/src/commands/drift.js +35 -35
- package/src/commands/init-plugin.js +6 -6
- package/src/commands/init.js +360 -360
- package/src/commands/rollback.js +126 -126
- package/src/commands/status.js +147 -147
- package/src/commands/sync.js +50 -50
- package/src/commands/update.js +24 -0
- package/src/commands/validate.js +349 -349
- package/src/hooks/block-dangerous.js +62 -62
- package/src/hooks/cfn-lint-after-edit-eval.js +30 -30
- package/src/hooks/cfn-lint-after-edit.js +81 -81
- package/src/hooks/check-catalog-fresh-eval.js +63 -63
- package/src/hooks/check-catalog-fresh.js +33 -33
- package/src/hooks/session-context-eval.js +81 -81
- package/src/hooks/session-context.js +41 -41
- package/src/hooks/subagent-context-eval.js +44 -44
- package/src/hooks/subagent-context.js +48 -48
- package/src/lib/audit-synthesis.js +24 -24
- package/src/lib/aws-client.js +15 -15
- package/src/lib/cfn-yaml.js +92 -92
- package/src/lib/config.js +76 -76
- package/src/lib/constants.js +85 -85
- package/src/lib/defaults.js +16 -16
- package/src/lib/install-workflow.js +28 -28
- package/src/lib/kit-source.js +43 -5
- package/src/lib/license-client.js +84 -84
- package/src/lib/license-config.js +162 -162
- package/src/lib/license-store.js +43 -8
- package/src/lib/license.js +15 -2
- package/src/lib/lint-overrides.js +226 -226
- package/src/lib/logger.js +16 -16
- package/src/lib/project-config.js +145 -145
- package/src/lib/providers/polar.js +215 -215
- package/src/lib/session-state.js +91 -91
package/src/lib/kit-source.js
CHANGED
|
@@ -9,12 +9,20 @@
|
|
|
9
9
|
// repaired with no network, which matters because `init plugin` is not
|
|
10
10
|
// something a buyer should have to be online to re-run.
|
|
11
11
|
|
|
12
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
12
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync as fsStatSync } from 'node:fs';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
14
|
import { unpackFiles } from './payload.js';
|
|
15
|
+
import { logger } from './logger.js';
|
|
15
16
|
|
|
16
17
|
export const KIT_CACHE_FILENAME = 'kit.bin';
|
|
17
18
|
|
|
19
|
+
// Backstop against a permanently-frozen cache: the kit is served by a pipeline
|
|
20
|
+
// SEPARATE from npm, so a version-stamped filename cannot catch "kit changed,
|
|
21
|
+
// CLI did not". After this window a re-run re-checks the service (and still
|
|
22
|
+
// falls back to the cached copy if the service is down). `--refresh` is the
|
|
23
|
+
// deterministic path; this only bounds how long a silent staleness can last.
|
|
24
|
+
export const KIT_CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24h
|
|
25
|
+
|
|
18
26
|
// Said on every service failure. Someone hitting this may have a stack stuck
|
|
19
27
|
// mid-update, and the first thing they need to know is that the emergency path
|
|
20
28
|
// is not what just broke. Those two commands are never licence-gated, and they
|
|
@@ -46,11 +54,41 @@ async function detailOf(resp) {
|
|
|
46
54
|
|
|
47
55
|
/**
|
|
48
56
|
* Resolve the content kit, from cache or from the service.
|
|
57
|
+
*
|
|
58
|
+
* A FRESH cache (younger than ttlMs, and no `refresh`) is served with no
|
|
59
|
+
* network — the common case, and what keeps `init plugin` offline-safe. A cold,
|
|
60
|
+
* stale, or `refresh`-forced call goes to the service; if that fails with an
|
|
61
|
+
* OUTAGE (network / 5xx / 429) but a cache exists, the cache is served with a
|
|
62
|
+
* warning rather than breaking the run. A REFUSAL (403/4xx) still throws — it is
|
|
63
|
+
* a deliberate "this licence cannot have the kit", not a blip to paper over.
|
|
64
|
+
*
|
|
49
65
|
* @returns {Promise<Array<{path: string, content: string}>>}
|
|
50
66
|
*/
|
|
51
|
-
export async function obtainKit({
|
|
67
|
+
export async function obtainKit({
|
|
68
|
+
licenseKey, serviceUrl, cacheDir,
|
|
69
|
+
fetch: fetchImpl = fetch,
|
|
70
|
+
refresh = false,
|
|
71
|
+
ttlMs = KIT_CACHE_TTL_MS,
|
|
72
|
+
now = Date.now,
|
|
73
|
+
statSync = fsStatSync,
|
|
74
|
+
warn = (m) => logger.warn(m),
|
|
75
|
+
}) {
|
|
52
76
|
const cachePath = join(cacheDir, KIT_CACHE_FILENAME);
|
|
53
|
-
|
|
77
|
+
const cached = existsSync(cachePath);
|
|
78
|
+
const fresh = cached && !refresh && (now() - statSync(cachePath).mtimeMs) < ttlMs;
|
|
79
|
+
|
|
80
|
+
if (fresh) return unpackFiles(readFileSync(cachePath));
|
|
81
|
+
|
|
82
|
+
// Serve the cache on an outage rather than breaking a re-run — but never on a
|
|
83
|
+
// deliberate refusal (below).
|
|
84
|
+
const fallbackOrThrow = (err) => {
|
|
85
|
+
if (cached) {
|
|
86
|
+
warn(`Could not refresh the plugin kit (${err.reason}) — using the cached copy. `
|
|
87
|
+
+ 'Re-run `clawform init plugin --refresh` once the service is reachable to pull the latest skills.');
|
|
88
|
+
return unpackFiles(readFileSync(cachePath));
|
|
89
|
+
}
|
|
90
|
+
throw outage(err.reason);
|
|
91
|
+
};
|
|
54
92
|
|
|
55
93
|
let resp;
|
|
56
94
|
try {
|
|
@@ -60,12 +98,12 @@ export async function obtainKit({ licenseKey, serviceUrl, cacheDir, fetch: fetch
|
|
|
60
98
|
body: JSON.stringify({ license_key: licenseKey }),
|
|
61
99
|
});
|
|
62
100
|
} catch (err) {
|
|
63
|
-
|
|
101
|
+
return fallbackOrThrow({ reason: err?.message ?? 'network error' });
|
|
64
102
|
}
|
|
65
103
|
|
|
66
104
|
// A throttle or a 5xx is the service failing, not the licence being judged —
|
|
67
105
|
// the same distinction the vendor client draws, and for the same reason.
|
|
68
|
-
if (resp.status === 429 || resp.status >= 500)
|
|
106
|
+
if (resp.status === 429 || resp.status >= 500) return fallbackOrThrow({ reason: `HTTP ${resp.status}` });
|
|
69
107
|
if (!resp.ok) throw refusal(await detailOf(resp));
|
|
70
108
|
|
|
71
109
|
const bundle = Buffer.from(await resp.arrayBuffer());
|
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
// Vendor-agnostic license client seam. Every consumer talks to this contract:
|
|
2
|
-
//
|
|
3
|
-
// activate(key, fingerprint) → { status:'valid', instanceId, holder? }
|
|
4
|
-
// | { status:'invalid', reason } // limit_reached | expired | not_found
|
|
5
|
-
// | { status:'error', reason } // vendor unreachable
|
|
6
|
-
// validate(key, activationId?) → { status:'valid'|'invalid'|'error', reason? }
|
|
7
|
-
// deactivate(key, activationId)→ { status:'valid'|'invalid'|'error', reason? }
|
|
8
|
-
//
|
|
9
|
-
// resolveClient() picks the implementation from the license mode:
|
|
10
|
-
// 'off' (default) → a dormant stub (configured:false) — assertLicensed
|
|
11
|
-
// passes everything, so the repo's own dev/CI and every
|
|
12
|
-
// buyer before launch is unaffected.
|
|
13
|
-
// 'sandbox'/'live' → the real Polar adapter (configured:true) against the
|
|
14
|
-
// matching base URL.
|
|
15
|
-
//
|
|
16
|
-
// Flipping the shipped default to a live mode is the single reviewed decision
|
|
17
|
-
// that turns enforcement on (see license-config.js).
|
|
18
|
-
|
|
19
|
-
import { existsSync } from 'node:fs';
|
|
20
|
-
import { join, dirname } from 'node:path';
|
|
21
|
-
import { fileURLToPath } from 'node:url';
|
|
22
|
-
import { readEnv } from './rename-compat.js';
|
|
23
|
-
import { createPolarClient } from './providers/polar.js';
|
|
24
|
-
import {
|
|
25
|
-
LICENSE_MODE_DEFAULT,
|
|
26
|
-
DEV_MARKER,
|
|
27
|
-
POLAR_ORGANIZATION_ID,
|
|
28
|
-
POLAR_BENEFIT_ID,
|
|
29
|
-
POLAR_TRIAL_BENEFIT_ID,
|
|
30
|
-
POLAR_BASE_URL,
|
|
31
|
-
} from './license-config.js';
|
|
32
|
-
|
|
33
|
-
const DORMANT = {
|
|
34
|
-
configured: false,
|
|
35
|
-
async activate() { return { status: 'error', reason: 'license enforcement is off in this build' }; },
|
|
36
|
-
async validate() { return { status: 'error', reason: 'license enforcement is off in this build' }; },
|
|
37
|
-
async deactivate() { return { status: 'error', reason: 'license enforcement is off in this build' }; },
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Resolved from THIS module's location, never from process.cwd(): with cwd, a
|
|
41
|
-
// buyer running clawform inside any repo that happens to carry the dev marker
|
|
42
|
-
// would be handed the dormant client — a bypass needing no edit at all.
|
|
43
|
-
const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
44
|
-
|
|
45
|
-
export function isSourceCheckout(packageRoot = PACKAGE_ROOT) {
|
|
46
|
-
return existsSync(join(packageRoot, DEV_MARKER));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// `env` stays POSITIONAL: callers pass an env object, and folding it into an
|
|
50
|
-
// options bag would silently reinterpret every existing call as `{env:
|
|
51
|
-
// undefined}` — falling back to process.env and passing for the wrong reason.
|
|
52
|
-
export function resolveMode(env = process.env, { packageRoot = PACKAGE_ROOT } = {}) {
|
|
53
|
-
const fromEnv = readEnv('LICENSE_MODE', env);
|
|
54
|
-
// The environment may pick BETWEEN enforcing modes; it can never turn
|
|
55
|
-
// enforcement down. Anything else — 'off', a typo, an empty string — is
|
|
56
|
-
// ignored rather than treated as a request to disable.
|
|
57
|
-
if (fromEnv === 'sandbox' || fromEnv === 'live') return fromEnv;
|
|
58
|
-
if (isSourceCheckout(packageRoot)) return 'off';
|
|
59
|
-
return LICENSE_MODE_DEFAULT;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Which benefits are trials, for the mode this install is running in. Resolved
|
|
63
|
-
// here rather than in license-config because that is where mode resolution
|
|
64
|
-
// already lives, and a second copy of it would be free to disagree.
|
|
65
|
-
//
|
|
66
|
-
// A dormant install has no trials: nothing to claim, nothing to refuse.
|
|
67
|
-
export function resolveTrialBenefitIds({ env = process.env, packageRoot = PACKAGE_ROOT } = {}) {
|
|
68
|
-
const mode = resolveMode(env, { packageRoot });
|
|
69
|
-
return POLAR_TRIAL_BENEFIT_ID[mode] ?? [];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function resolveClient({ env = process.env, packageRoot = PACKAGE_ROOT, fetch } = {}) {
|
|
73
|
-
const mode = resolveMode(env, { packageRoot });
|
|
74
|
-
if (mode !== 'sandbox' && mode !== 'live') return DORMANT;
|
|
75
|
-
// Both looked up by the same mode, so they cannot drift apart: a base URL
|
|
76
|
-
// for one environment with an organization from the other is the failure
|
|
77
|
-
// this shape exists to make impossible.
|
|
78
|
-
return createPolarClient({
|
|
79
|
-
baseUrl: POLAR_BASE_URL[mode],
|
|
80
|
-
organizationId: POLAR_ORGANIZATION_ID[mode],
|
|
81
|
-
benefitIds: POLAR_BENEFIT_ID[mode],
|
|
82
|
-
...(fetch ? { fetch } : {}),
|
|
83
|
-
});
|
|
84
|
-
}
|
|
1
|
+
// Vendor-agnostic license client seam. Every consumer talks to this contract:
|
|
2
|
+
//
|
|
3
|
+
// activate(key, fingerprint) → { status:'valid', instanceId, holder? }
|
|
4
|
+
// | { status:'invalid', reason } // limit_reached | expired | not_found
|
|
5
|
+
// | { status:'error', reason } // vendor unreachable
|
|
6
|
+
// validate(key, activationId?) → { status:'valid'|'invalid'|'error', reason? }
|
|
7
|
+
// deactivate(key, activationId)→ { status:'valid'|'invalid'|'error', reason? }
|
|
8
|
+
//
|
|
9
|
+
// resolveClient() picks the implementation from the license mode:
|
|
10
|
+
// 'off' (default) → a dormant stub (configured:false) — assertLicensed
|
|
11
|
+
// passes everything, so the repo's own dev/CI and every
|
|
12
|
+
// buyer before launch is unaffected.
|
|
13
|
+
// 'sandbox'/'live' → the real Polar adapter (configured:true) against the
|
|
14
|
+
// matching base URL.
|
|
15
|
+
//
|
|
16
|
+
// Flipping the shipped default to a live mode is the single reviewed decision
|
|
17
|
+
// that turns enforcement on (see license-config.js).
|
|
18
|
+
|
|
19
|
+
import { existsSync } from 'node:fs';
|
|
20
|
+
import { join, dirname } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import { readEnv } from './rename-compat.js';
|
|
23
|
+
import { createPolarClient } from './providers/polar.js';
|
|
24
|
+
import {
|
|
25
|
+
LICENSE_MODE_DEFAULT,
|
|
26
|
+
DEV_MARKER,
|
|
27
|
+
POLAR_ORGANIZATION_ID,
|
|
28
|
+
POLAR_BENEFIT_ID,
|
|
29
|
+
POLAR_TRIAL_BENEFIT_ID,
|
|
30
|
+
POLAR_BASE_URL,
|
|
31
|
+
} from './license-config.js';
|
|
32
|
+
|
|
33
|
+
const DORMANT = {
|
|
34
|
+
configured: false,
|
|
35
|
+
async activate() { return { status: 'error', reason: 'license enforcement is off in this build' }; },
|
|
36
|
+
async validate() { return { status: 'error', reason: 'license enforcement is off in this build' }; },
|
|
37
|
+
async deactivate() { return { status: 'error', reason: 'license enforcement is off in this build' }; },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Resolved from THIS module's location, never from process.cwd(): with cwd, a
|
|
41
|
+
// buyer running clawform inside any repo that happens to carry the dev marker
|
|
42
|
+
// would be handed the dormant client — a bypass needing no edit at all.
|
|
43
|
+
const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
44
|
+
|
|
45
|
+
export function isSourceCheckout(packageRoot = PACKAGE_ROOT) {
|
|
46
|
+
return existsSync(join(packageRoot, DEV_MARKER));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// `env` stays POSITIONAL: callers pass an env object, and folding it into an
|
|
50
|
+
// options bag would silently reinterpret every existing call as `{env:
|
|
51
|
+
// undefined}` — falling back to process.env and passing for the wrong reason.
|
|
52
|
+
export function resolveMode(env = process.env, { packageRoot = PACKAGE_ROOT } = {}) {
|
|
53
|
+
const fromEnv = readEnv('LICENSE_MODE', env);
|
|
54
|
+
// The environment may pick BETWEEN enforcing modes; it can never turn
|
|
55
|
+
// enforcement down. Anything else — 'off', a typo, an empty string — is
|
|
56
|
+
// ignored rather than treated as a request to disable.
|
|
57
|
+
if (fromEnv === 'sandbox' || fromEnv === 'live') return fromEnv;
|
|
58
|
+
if (isSourceCheckout(packageRoot)) return 'off';
|
|
59
|
+
return LICENSE_MODE_DEFAULT;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Which benefits are trials, for the mode this install is running in. Resolved
|
|
63
|
+
// here rather than in license-config because that is where mode resolution
|
|
64
|
+
// already lives, and a second copy of it would be free to disagree.
|
|
65
|
+
//
|
|
66
|
+
// A dormant install has no trials: nothing to claim, nothing to refuse.
|
|
67
|
+
export function resolveTrialBenefitIds({ env = process.env, packageRoot = PACKAGE_ROOT } = {}) {
|
|
68
|
+
const mode = resolveMode(env, { packageRoot });
|
|
69
|
+
return POLAR_TRIAL_BENEFIT_ID[mode] ?? [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function resolveClient({ env = process.env, packageRoot = PACKAGE_ROOT, fetch } = {}) {
|
|
73
|
+
const mode = resolveMode(env, { packageRoot });
|
|
74
|
+
if (mode !== 'sandbox' && mode !== 'live') return DORMANT;
|
|
75
|
+
// Both looked up by the same mode, so they cannot drift apart: a base URL
|
|
76
|
+
// for one environment with an organization from the other is the failure
|
|
77
|
+
// this shape exists to make impossible.
|
|
78
|
+
return createPolarClient({
|
|
79
|
+
baseUrl: POLAR_BASE_URL[mode],
|
|
80
|
+
organizationId: POLAR_ORGANIZATION_ID[mode],
|
|
81
|
+
benefitIds: POLAR_BENEFIT_ID[mode],
|
|
82
|
+
...(fetch ? { fetch } : {}),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -1,162 +1,162 @@
|
|
|
1
|
-
// Licensing time policy. Vendor identifiers (org/product IDs) join this file
|
|
2
|
-
// once the payout test settles which vendor we ship with — the time policy is
|
|
3
|
-
// vendor-independent and the grace/revalidate logic only needs these two.
|
|
4
|
-
|
|
5
|
-
// How long a cached "valid" verdict is trusted before the CLI re-checks with
|
|
6
|
-
// the vendor in the background. Once per day keeps revocation reasonably fresh
|
|
7
|
-
// without turning every command into a network call.
|
|
8
|
-
export const REVALIDATE_HOURS = 24;
|
|
9
|
-
|
|
10
|
-
// How long a buyer keeps working after the LAST successful validation when the
|
|
11
|
-
// vendor cannot be reached (offline dev box, vendor outage). Two weeks: long
|
|
12
|
-
// enough to survive a vacation offline, short enough that a revoked-but-
|
|
13
|
-
// unreachable key does not run forever.
|
|
14
|
-
export const GRACE_DAYS = 14;
|
|
15
|
-
|
|
16
|
-
// --- Vendor: Polar -------------------------------------------------------
|
|
17
|
-
// The organization id is not a secret — it is a public parameter of every
|
|
18
|
-
// customer-portal call, and the API needs no server token besides the buyer's
|
|
19
|
-
// own key. Safe to ship in the tarball.
|
|
20
|
-
//
|
|
21
|
-
// Keyed by mode, and it has to be. Polar's sandbox is a separate system rather
|
|
22
|
-
// than a flag on the same one: you sign up for it separately, the organization
|
|
23
|
-
// you create there does not exist in production, and its id is different. A
|
|
24
|
-
// single id shared across both modes builds a client that talks to
|
|
25
|
-
// api.polar.sh while naming a sandbox organization — every activation by a
|
|
26
|
-
// real buyer fails, and it fails on the first day of selling rather than in
|
|
27
|
-
// any amount of sandbox testing. Paired with POLAR_BASE_URL below so the two
|
|
28
|
-
// can only be changed together.
|
|
29
|
-
export const POLAR_ORGANIZATION_ID = {
|
|
30
|
-
live: '75b98aba-8768-4534-9b76-dd7f16cba3d3',
|
|
31
|
-
sandbox: 'c234fc61-20e4-4d6f-b919-0f5823f7984d',
|
|
32
|
-
};
|
|
33
|
-
export const POLAR_BASE_URL = {
|
|
34
|
-
live: 'https://api.polar.sh',
|
|
35
|
-
sandbox: 'https://sandbox-api.polar.sh',
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// The License Keys benefit this product sells. The organization is not enough
|
|
39
|
-
// to identify a key: every product under the same organization validates
|
|
40
|
-
// against the same endpoint with the same organization id, so the moment a
|
|
41
|
-
// second product sells from here, a key bought for THAT product activates this
|
|
42
|
-
// one. A paying customer of one tool silently unlocking another is not a bug
|
|
43
|
-
// anyone reports.
|
|
44
|
-
//
|
|
45
|
-
// Polar returns the benefit on both activate and validate, so the check costs
|
|
46
|
-
// nothing and closes it. Keyed by mode for the same reason the organization is
|
|
47
|
-
// — a sandbox benefit does not exist in production.
|
|
48
|
-
//
|
|
49
|
-
// A LIST per mode, one entry per paid tier. Polar carries limit_activations on
|
|
50
|
-
// the benefit, not the product, so tiers with different device caps cannot share
|
|
51
|
-
// one — the individual seat and the team seat are necessarily separate benefits.
|
|
52
|
-
// A key is accepted when it matches ANY entry; the tier it belongs to is Polar's
|
|
53
|
-
// business, not the CLI's.
|
|
54
|
-
//
|
|
55
|
-
// Nothing may appear in both lists: an id reachable from either environment
|
|
56
|
-
// would let a sandbox key unlock a live install.
|
|
57
|
-
export const POLAR_BENEFIT_ID = {
|
|
58
|
-
// [0] individual seat, [1] team seat. Order is not significance — a key is
|
|
59
|
-
// accepted on any match — but it is the order the walk tries, so the tier
|
|
60
|
-
// that sells most often belongs first.
|
|
61
|
-
live: [
|
|
62
|
-
'b85a9271-22ac-45b3-ab0a-b0c93df6b13c',
|
|
63
|
-
'be046860-7be4-43bd-ac8b-cf47d08f77d0',
|
|
64
|
-
'a3fc4e93-9e93-4317-bed5-5b44a5af9e19',
|
|
65
|
-
],
|
|
66
|
-
sandbox: [
|
|
67
|
-
'69d1ccaa-65c4-48da-ad4a-64961772c17e',
|
|
68
|
-
'a540043d-da17-48ae-bee6-e9a900fbe1cc',
|
|
69
|
-
'9c5fe82d-09a7-490c-91e3-47d164603423',
|
|
70
|
-
],
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// Which of the benefits above are TRIALS. A trial key validates like any other
|
|
74
|
-
// — it has to appear in POLAR_BENEFIT_ID as well — but it additionally claims
|
|
75
|
-
// the machine, so one device cannot start a second trial under a new email.
|
|
76
|
-
//
|
|
77
|
-
// Listed separately rather than encoded in the entries above so phase 01's
|
|
78
|
-
// shape and its tests stay untouched. A test pins that every id here also
|
|
79
|
-
// appears in POLAR_BENEFIT_ID: an id in only one list is a trial key nobody can
|
|
80
|
-
// activate, or a paid key that silently burns a device claim.
|
|
81
|
-
//
|
|
82
|
-
// The licence-key benefit on each trial product must be VISIBLE in Polar: the
|
|
83
|
-
// buyer has to read the key out of the portal or the confirmation email to type
|
|
84
|
-
// it into `clawform activate`. A hidden benefit is granted and unusable.
|
|
85
|
-
export const POLAR_TRIAL_BENEFIT_ID = {
|
|
86
|
-
live: ['a3fc4e93-9e93-4317-bed5-5b44a5af9e19'],
|
|
87
|
-
sandbox: ['9c5fe82d-09a7-490c-91e3-47d164603423'],
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
// --- Content kit delivery -----------------------------------------------
|
|
91
|
-
// The plugin content (skills/commands/agents/rules/templates) is NOT in the npm
|
|
92
|
-
// tarball. It is fetched by `clawform init plugin` from this service, which
|
|
93
|
-
// checks the licence with the vendor before serving anything.
|
|
94
|
-
//
|
|
95
|
-
// The previous model shipped the content as an encrypted blob with its key
|
|
96
|
-
// beside it in the same tarball — which protected nothing: `npm i` handed a
|
|
97
|
-
// non-buyer both halves. There is no key here now because the CLI never
|
|
98
|
-
// decrypts; the bundle lives in a private bucket and travels over TLS.
|
|
99
|
-
//
|
|
100
|
-
// Not keyed by licence mode. There is one kit, and which licences may have it
|
|
101
|
-
// is the service's judgement, not a build-time constant.
|
|
102
|
-
export const KIT_SERVICE_URL = 'https://kit.thejoseki.com';
|
|
103
|
-
|
|
104
|
-
// Written by scripts/pack-payload.js for upload, and cached under the vendor
|
|
105
|
-
// home after a successful fetch. Never present in the published package.
|
|
106
|
-
export const KIT_FILENAME = 'kit.bin';
|
|
107
|
-
|
|
108
|
-
// Signs the local licence record. The gate trusts an 'active' record with a
|
|
109
|
-
// fresh timestamp without calling the vendor, so an unsigned cache would make
|
|
110
|
-
// `echo '{"status":"active",…}' > ~/.clawform/license.json` a complete bypass —
|
|
111
|
-
// and a shareable one-liner at that. Signing raises it to "extract this secret
|
|
112
|
-
// from the CLI first", the same bar as the dev marker.
|
|
113
|
-
//
|
|
114
|
-
// Same 1a honesty as PAYLOAD_KEY_HEX: the secret ships with the code, so a
|
|
115
|
-
// determined attacker can still forge. The goal is killing the one-liner class.
|
|
116
|
-
// Distinct from the payload key on purpose — keys with different jobs.
|
|
117
|
-
//
|
|
118
|
-
// Rotates alongside the payload key, and for the same reason: a release that
|
|
119
|
-
// published this value hands every later release a forged 'active' record.
|
|
120
|
-
// The cost is that records signed with the old secret read as unlicensed, so a
|
|
121
|
-
// buyer re-activates — cheap now, and it only gets dearer with every seat sold.
|
|
122
|
-
export const LICENSE_RECORD_SECRET_HEX = 'c4179ff7c3e2ebd223fdb2653ed1163f5b8cb89c7900af2a88f186fbb55818e5';
|
|
123
|
-
|
|
124
|
-
// Tolerance for a machine clock running slightly ahead of the vendor's. Beyond
|
|
125
|
-
// it, a future `lastValidatedAt` is treated as needing revalidation rather than
|
|
126
|
-
// as an eternally fresh verdict.
|
|
127
|
-
export const CLOCK_SKEW_HOURS = 1;
|
|
128
|
-
|
|
129
|
-
// How a build tells whether it is a source checkout or an installed package.
|
|
130
|
-
// This file is the payload build script — `package.json` `files` never ships
|
|
131
|
-
// `scripts/`, so it is present in a clone and absent in `npm i -g @thejoseki/clawform`.
|
|
132
|
-
//
|
|
133
|
-
// Dormancy is granted by the install SHAPE, not by anything a buyer can set:
|
|
134
|
-
// a `CLAWFORM_LICENSE_MODE=off` that disabled the gate would be a
|
|
135
|
-
// copy-pasteable one-liner, the one bypass class that actually spreads.
|
|
136
|
-
// Faking dormancy now means creating this file inside the installed package —
|
|
137
|
-
// an edit, not a one-liner.
|
|
138
|
-
//
|
|
139
|
-
// ⚠️ Adding `scripts/` to `package.json` `files` would ship the marker and
|
|
140
|
-
// silently disable enforcement for everyone. A packaging test asserts it stays
|
|
141
|
-
// out of the tarball.
|
|
142
|
-
export const DEV_MARKER = 'scripts/pack-payload.js';
|
|
143
|
-
|
|
144
|
-
// The master switch, and the single decision that turns enforcement on.
|
|
145
|
-
//
|
|
146
|
-
// Held at 'off' through development, because enforcing before a key could be
|
|
147
|
-
// bought would have locked out every user with no way back in. It moved to
|
|
148
|
-
// 'live' only once all three preconditions were verified against the
|
|
149
|
-
// production vendor rather than assumed:
|
|
150
|
-
//
|
|
151
|
-
// - a Polar product exists with a License Keys benefit, activation limit 3
|
|
152
|
-
// - a real purchased key drives activate / validate / deactivate, the
|
|
153
|
-
// three-device cap refuses a fourth machine, and deactivate demonstrably
|
|
154
|
-
// frees a slot (proved by re-activating, not by the return code)
|
|
155
|
-
// - the sandbox and production environments reject each other's keys, which
|
|
156
|
-
// is what makes the per-mode organization and benefit ids load-bearing
|
|
157
|
-
//
|
|
158
|
-
// A source checkout stays dormant regardless: that exemption comes from the
|
|
159
|
-
// dev marker's presence, not from this value, so contributors need no key.
|
|
160
|
-
// Overridable per run via CLAWFORM_LICENSE_MODE, which can raise enforcement
|
|
161
|
-
// but never lower it.
|
|
162
|
-
export const LICENSE_MODE_DEFAULT = 'live';
|
|
1
|
+
// Licensing time policy. Vendor identifiers (org/product IDs) join this file
|
|
2
|
+
// once the payout test settles which vendor we ship with — the time policy is
|
|
3
|
+
// vendor-independent and the grace/revalidate logic only needs these two.
|
|
4
|
+
|
|
5
|
+
// How long a cached "valid" verdict is trusted before the CLI re-checks with
|
|
6
|
+
// the vendor in the background. Once per day keeps revocation reasonably fresh
|
|
7
|
+
// without turning every command into a network call.
|
|
8
|
+
export const REVALIDATE_HOURS = 24;
|
|
9
|
+
|
|
10
|
+
// How long a buyer keeps working after the LAST successful validation when the
|
|
11
|
+
// vendor cannot be reached (offline dev box, vendor outage). Two weeks: long
|
|
12
|
+
// enough to survive a vacation offline, short enough that a revoked-but-
|
|
13
|
+
// unreachable key does not run forever.
|
|
14
|
+
export const GRACE_DAYS = 14;
|
|
15
|
+
|
|
16
|
+
// --- Vendor: Polar -------------------------------------------------------
|
|
17
|
+
// The organization id is not a secret — it is a public parameter of every
|
|
18
|
+
// customer-portal call, and the API needs no server token besides the buyer's
|
|
19
|
+
// own key. Safe to ship in the tarball.
|
|
20
|
+
//
|
|
21
|
+
// Keyed by mode, and it has to be. Polar's sandbox is a separate system rather
|
|
22
|
+
// than a flag on the same one: you sign up for it separately, the organization
|
|
23
|
+
// you create there does not exist in production, and its id is different. A
|
|
24
|
+
// single id shared across both modes builds a client that talks to
|
|
25
|
+
// api.polar.sh while naming a sandbox organization — every activation by a
|
|
26
|
+
// real buyer fails, and it fails on the first day of selling rather than in
|
|
27
|
+
// any amount of sandbox testing. Paired with POLAR_BASE_URL below so the two
|
|
28
|
+
// can only be changed together.
|
|
29
|
+
export const POLAR_ORGANIZATION_ID = {
|
|
30
|
+
live: '75b98aba-8768-4534-9b76-dd7f16cba3d3',
|
|
31
|
+
sandbox: 'c234fc61-20e4-4d6f-b919-0f5823f7984d',
|
|
32
|
+
};
|
|
33
|
+
export const POLAR_BASE_URL = {
|
|
34
|
+
live: 'https://api.polar.sh',
|
|
35
|
+
sandbox: 'https://sandbox-api.polar.sh',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// The License Keys benefit this product sells. The organization is not enough
|
|
39
|
+
// to identify a key: every product under the same organization validates
|
|
40
|
+
// against the same endpoint with the same organization id, so the moment a
|
|
41
|
+
// second product sells from here, a key bought for THAT product activates this
|
|
42
|
+
// one. A paying customer of one tool silently unlocking another is not a bug
|
|
43
|
+
// anyone reports.
|
|
44
|
+
//
|
|
45
|
+
// Polar returns the benefit on both activate and validate, so the check costs
|
|
46
|
+
// nothing and closes it. Keyed by mode for the same reason the organization is
|
|
47
|
+
// — a sandbox benefit does not exist in production.
|
|
48
|
+
//
|
|
49
|
+
// A LIST per mode, one entry per paid tier. Polar carries limit_activations on
|
|
50
|
+
// the benefit, not the product, so tiers with different device caps cannot share
|
|
51
|
+
// one — the individual seat and the team seat are necessarily separate benefits.
|
|
52
|
+
// A key is accepted when it matches ANY entry; the tier it belongs to is Polar's
|
|
53
|
+
// business, not the CLI's.
|
|
54
|
+
//
|
|
55
|
+
// Nothing may appear in both lists: an id reachable from either environment
|
|
56
|
+
// would let a sandbox key unlock a live install.
|
|
57
|
+
export const POLAR_BENEFIT_ID = {
|
|
58
|
+
// [0] individual seat, [1] team seat. Order is not significance — a key is
|
|
59
|
+
// accepted on any match — but it is the order the walk tries, so the tier
|
|
60
|
+
// that sells most often belongs first.
|
|
61
|
+
live: [
|
|
62
|
+
'b85a9271-22ac-45b3-ab0a-b0c93df6b13c',
|
|
63
|
+
'be046860-7be4-43bd-ac8b-cf47d08f77d0',
|
|
64
|
+
'a3fc4e93-9e93-4317-bed5-5b44a5af9e19',
|
|
65
|
+
],
|
|
66
|
+
sandbox: [
|
|
67
|
+
'69d1ccaa-65c4-48da-ad4a-64961772c17e',
|
|
68
|
+
'a540043d-da17-48ae-bee6-e9a900fbe1cc',
|
|
69
|
+
'9c5fe82d-09a7-490c-91e3-47d164603423',
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Which of the benefits above are TRIALS. A trial key validates like any other
|
|
74
|
+
// — it has to appear in POLAR_BENEFIT_ID as well — but it additionally claims
|
|
75
|
+
// the machine, so one device cannot start a second trial under a new email.
|
|
76
|
+
//
|
|
77
|
+
// Listed separately rather than encoded in the entries above so phase 01's
|
|
78
|
+
// shape and its tests stay untouched. A test pins that every id here also
|
|
79
|
+
// appears in POLAR_BENEFIT_ID: an id in only one list is a trial key nobody can
|
|
80
|
+
// activate, or a paid key that silently burns a device claim.
|
|
81
|
+
//
|
|
82
|
+
// The licence-key benefit on each trial product must be VISIBLE in Polar: the
|
|
83
|
+
// buyer has to read the key out of the portal or the confirmation email to type
|
|
84
|
+
// it into `clawform activate`. A hidden benefit is granted and unusable.
|
|
85
|
+
export const POLAR_TRIAL_BENEFIT_ID = {
|
|
86
|
+
live: ['a3fc4e93-9e93-4317-bed5-5b44a5af9e19'],
|
|
87
|
+
sandbox: ['9c5fe82d-09a7-490c-91e3-47d164603423'],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// --- Content kit delivery -----------------------------------------------
|
|
91
|
+
// The plugin content (skills/commands/agents/rules/templates) is NOT in the npm
|
|
92
|
+
// tarball. It is fetched by `clawform init plugin` from this service, which
|
|
93
|
+
// checks the licence with the vendor before serving anything.
|
|
94
|
+
//
|
|
95
|
+
// The previous model shipped the content as an encrypted blob with its key
|
|
96
|
+
// beside it in the same tarball — which protected nothing: `npm i` handed a
|
|
97
|
+
// non-buyer both halves. There is no key here now because the CLI never
|
|
98
|
+
// decrypts; the bundle lives in a private bucket and travels over TLS.
|
|
99
|
+
//
|
|
100
|
+
// Not keyed by licence mode. There is one kit, and which licences may have it
|
|
101
|
+
// is the service's judgement, not a build-time constant.
|
|
102
|
+
export const KIT_SERVICE_URL = 'https://kit.thejoseki.com';
|
|
103
|
+
|
|
104
|
+
// Written by scripts/pack-payload.js for upload, and cached under the vendor
|
|
105
|
+
// home after a successful fetch. Never present in the published package.
|
|
106
|
+
export const KIT_FILENAME = 'kit.bin';
|
|
107
|
+
|
|
108
|
+
// Signs the local licence record. The gate trusts an 'active' record with a
|
|
109
|
+
// fresh timestamp without calling the vendor, so an unsigned cache would make
|
|
110
|
+
// `echo '{"status":"active",…}' > ~/.clawform/license.json` a complete bypass —
|
|
111
|
+
// and a shareable one-liner at that. Signing raises it to "extract this secret
|
|
112
|
+
// from the CLI first", the same bar as the dev marker.
|
|
113
|
+
//
|
|
114
|
+
// Same 1a honesty as PAYLOAD_KEY_HEX: the secret ships with the code, so a
|
|
115
|
+
// determined attacker can still forge. The goal is killing the one-liner class.
|
|
116
|
+
// Distinct from the payload key on purpose — keys with different jobs.
|
|
117
|
+
//
|
|
118
|
+
// Rotates alongside the payload key, and for the same reason: a release that
|
|
119
|
+
// published this value hands every later release a forged 'active' record.
|
|
120
|
+
// The cost is that records signed with the old secret read as unlicensed, so a
|
|
121
|
+
// buyer re-activates — cheap now, and it only gets dearer with every seat sold.
|
|
122
|
+
export const LICENSE_RECORD_SECRET_HEX = 'c4179ff7c3e2ebd223fdb2653ed1163f5b8cb89c7900af2a88f186fbb55818e5';
|
|
123
|
+
|
|
124
|
+
// Tolerance for a machine clock running slightly ahead of the vendor's. Beyond
|
|
125
|
+
// it, a future `lastValidatedAt` is treated as needing revalidation rather than
|
|
126
|
+
// as an eternally fresh verdict.
|
|
127
|
+
export const CLOCK_SKEW_HOURS = 1;
|
|
128
|
+
|
|
129
|
+
// How a build tells whether it is a source checkout or an installed package.
|
|
130
|
+
// This file is the payload build script — `package.json` `files` never ships
|
|
131
|
+
// `scripts/`, so it is present in a clone and absent in `npm i -g @thejoseki/clawform`.
|
|
132
|
+
//
|
|
133
|
+
// Dormancy is granted by the install SHAPE, not by anything a buyer can set:
|
|
134
|
+
// a `CLAWFORM_LICENSE_MODE=off` that disabled the gate would be a
|
|
135
|
+
// copy-pasteable one-liner, the one bypass class that actually spreads.
|
|
136
|
+
// Faking dormancy now means creating this file inside the installed package —
|
|
137
|
+
// an edit, not a one-liner.
|
|
138
|
+
//
|
|
139
|
+
// ⚠️ Adding `scripts/` to `package.json` `files` would ship the marker and
|
|
140
|
+
// silently disable enforcement for everyone. A packaging test asserts it stays
|
|
141
|
+
// out of the tarball.
|
|
142
|
+
export const DEV_MARKER = 'scripts/pack-payload.js';
|
|
143
|
+
|
|
144
|
+
// The master switch, and the single decision that turns enforcement on.
|
|
145
|
+
//
|
|
146
|
+
// Held at 'off' through development, because enforcing before a key could be
|
|
147
|
+
// bought would have locked out every user with no way back in. It moved to
|
|
148
|
+
// 'live' only once all three preconditions were verified against the
|
|
149
|
+
// production vendor rather than assumed:
|
|
150
|
+
//
|
|
151
|
+
// - a Polar product exists with a License Keys benefit, activation limit 3
|
|
152
|
+
// - a real purchased key drives activate / validate / deactivate, the
|
|
153
|
+
// three-device cap refuses a fourth machine, and deactivate demonstrably
|
|
154
|
+
// frees a slot (proved by re-activating, not by the return code)
|
|
155
|
+
// - the sandbox and production environments reject each other's keys, which
|
|
156
|
+
// is what makes the per-mode organization and benefit ids load-bearing
|
|
157
|
+
//
|
|
158
|
+
// A source checkout stays dormant regardless: that exemption comes from the
|
|
159
|
+
// dev marker's presence, not from this value, so contributors need no key.
|
|
160
|
+
// Overridable per run via CLAWFORM_LICENSE_MODE, which can raise enforcement
|
|
161
|
+
// but never lower it.
|
|
162
|
+
export const LICENSE_MODE_DEFAULT = 'live';
|