@clickraft/cli 0.1.5 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -1,8 +1,8 @@
1
- ## [0.1.5](https://github.com/clickraft/cli/compare/v0.1.4...v0.1.5) (2026-05-23)
1
+ ## [0.1.6](https://github.com/clickraft/cli/compare/v0.1.5...v0.1.6) (2026-05-23)
2
2
 
3
3
  ### Bug Fixes
4
4
 
5
- * **tokens:** align /tokens list response to { data: T[] } envelope ([72d834d](https://github.com/clickraft/cli/commit/72d834db182b884b740aebc38153e6a0c51db95b))
5
+ * **client:** support Cloudflare Access service tokens via env vars ([4141f21](https://github.com/clickraft/cli/commit/4141f21a3c53f5795fef41f3bd876670563968f1))
6
6
 
7
7
  # Changelog
8
8
 
package/README.md CHANGED
@@ -21,6 +21,19 @@ clickraft generate create --prompt "a serene mountain landscape at dawn"
21
21
 
22
22
  Full documentation: [docs.clickraft.ai/cli](https://docs.clickraft.ai/cli) _(coming soon)_
23
23
 
24
+ ## Connecting to staging
25
+
26
+ The staging API (`https://staging.clickraft.ai/api/agents/v1`) sits behind Cloudflare Access Zero Trust. To authenticate non-interactively, create a service token in the Cloudflare Zero Trust dashboard (**Access → Service Auth → Service Tokens**), then export both halves before invoking the CLI:
27
+
28
+ ```bash
29
+ export CLOUDFLARE_ACCESS_CLIENT_ID="<client-id>.access"
30
+ export CLOUDFLARE_ACCESS_CLIENT_SECRET="<client-secret>"
31
+
32
+ clickraft tokens list --profile staging
33
+ ```
34
+
35
+ Both env vars must be set together — if only one is set the CLI exits with a configuration error. Production usage (the default) does not require these env vars.
36
+
24
37
  ## Use with AI coding agents
25
38
 
26
39
  The Clickraft CLI is designed to work with Claude Code, Cursor, Codex, and other AI coding agents. Install the skills package for your agent:
package/dist/cli.js CHANGED
@@ -529,6 +529,10 @@ var ApiClient = class {
529
529
  if (this.options.accessToken) {
530
530
  h.authorization = `Bearer ${this.options.accessToken}`;
531
531
  }
532
+ if (this.options.cloudflareAccess) {
533
+ h["cf-access-client-id"] = this.options.cloudflareAccess.clientId;
534
+ h["cf-access-client-secret"] = this.options.cloudflareAccess.clientSecret;
535
+ }
532
536
  if (opts.body !== void 0 && opts.method !== "GET" && opts.method !== "DELETE") {
533
537
  h["content-type"] = "application/json";
534
538
  }
@@ -1173,8 +1177,11 @@ var TOKEN_ENV_VAR = "CLICKRAFT_TOKEN";
1173
1177
  var PROFILE_ENV_VAR = "CLICKRAFT_PROFILE";
1174
1178
  var API_BASE_URL_ENV_VAR = "CLICKRAFT_API_BASE_URL";
1175
1179
  var TELEMETRY_DISABLE_ENV_VAR = "CLICKRAFT_DISABLE_TELEMETRY";
1180
+ var CLOUDFLARE_ACCESS_CLIENT_ID_ENV_VAR = "CLOUDFLARE_ACCESS_CLIENT_ID";
1181
+ var CLOUDFLARE_ACCESS_CLIENT_SECRET_ENV_VAR = "CLOUDFLARE_ACCESS_CLIENT_SECRET";
1176
1182
  async function loadConfig(options = {}) {
1177
1183
  const env = options.env ?? process.env;
1184
+ const cloudflareAccess = loadCloudflareAccess(env);
1178
1185
  const flagToken = trim(options.flagToken);
1179
1186
  const envToken = trim(env[TOKEN_ENV_VAR]);
1180
1187
  const flagProfile = trim(options.flagProfile);
@@ -1204,9 +1211,26 @@ async function loadConfig(options = {}) {
1204
1211
  return {
1205
1212
  accessToken: tokenFromFlagOrEnv ?? fileAccessToken ?? null,
1206
1213
  profileName: profileName ?? fileProfileName ?? DEFAULT_PROFILE_NAME,
1207
- apiBaseUrl: apiBaseFromFlagOrEnv ?? fileApiBaseUrl ?? DEFAULT_API_BASE_URL
1214
+ apiBaseUrl: apiBaseFromFlagOrEnv ?? fileApiBaseUrl ?? DEFAULT_API_BASE_URL,
1215
+ cloudflareAccess
1208
1216
  };
1209
1217
  }
1218
+ function loadCloudflareAccess(env) {
1219
+ const clientId = trim(env[CLOUDFLARE_ACCESS_CLIENT_ID_ENV_VAR]);
1220
+ const clientSecret = trim(env[CLOUDFLARE_ACCESS_CLIENT_SECRET_ENV_VAR]);
1221
+ if (clientId && clientSecret) {
1222
+ return { clientId, clientSecret };
1223
+ }
1224
+ if (!clientId && !clientSecret) {
1225
+ return null;
1226
+ }
1227
+ const onlySet = clientId ? CLOUDFLARE_ACCESS_CLIENT_ID_ENV_VAR : CLOUDFLARE_ACCESS_CLIENT_SECRET_ENV_VAR;
1228
+ throw new ApiError(
1229
+ "INPUT_INVALID_FORMAT",
1230
+ `${CLOUDFLARE_ACCESS_CLIENT_ID_ENV_VAR} and ${CLOUDFLARE_ACCESS_CLIENT_SECRET_ENV_VAR} must both be set, or neither. Got only ${onlySet}.`,
1231
+ { retryable: false }
1232
+ );
1233
+ }
1210
1234
  function trim(value) {
1211
1235
  if (value === void 0) return void 0;
1212
1236
  const t = value.trim();
@@ -1300,7 +1324,8 @@ async function buildClient(opts) {
1300
1324
  return new ApiClient({
1301
1325
  apiBaseUrl: cfg.apiBaseUrl,
1302
1326
  accessToken: cfg.accessToken,
1303
- signal: opts.signal
1327
+ signal: opts.signal,
1328
+ cloudflareAccess: cfg.cloudflareAccess
1304
1329
  });
1305
1330
  }
1306
1331
  async function postAssetMetadata(args) {
@@ -1466,7 +1491,8 @@ async function buildGenerateClient(opts) {
1466
1491
  return new ApiClient({
1467
1492
  apiBaseUrl: cfg.apiBaseUrl,
1468
1493
  accessToken: cfg.accessToken,
1469
- signal: opts.signal
1494
+ signal: opts.signal,
1495
+ cloudflareAccess: cfg.cloudflareAccess
1470
1496
  });
1471
1497
  }
1472
1498
  function isUrlLike(value) {
@@ -2073,7 +2099,12 @@ async function logout(options) {
2073
2099
  let serverRevoked = false;
2074
2100
  let revokeFailureMessage;
2075
2101
  if (options.revoke) {
2076
- const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => new ApiClient({ apiBaseUrl, accessToken, signal: options.signal }));
2102
+ const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => new ApiClient({
2103
+ apiBaseUrl,
2104
+ accessToken,
2105
+ signal: options.signal,
2106
+ cloudflareAccess: config.cloudflareAccess
2107
+ }));
2077
2108
  const client = factory(profile.apiBaseUrl, profile.accessToken);
2078
2109
  try {
2079
2110
  await client.request({
@@ -2198,7 +2229,12 @@ async function listTokens(options) {
2198
2229
  if (!config.accessToken) {
2199
2230
  throw new AuthError("AUTH_TOKEN_MISSING", `Not authenticated. Run \`clickraft login\` first.`);
2200
2231
  }
2201
- const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => new ApiClient({ apiBaseUrl, accessToken, signal: options.signal }));
2232
+ const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => new ApiClient({
2233
+ apiBaseUrl,
2234
+ accessToken,
2235
+ signal: options.signal,
2236
+ cloudflareAccess: config.cloudflareAccess
2237
+ }));
2202
2238
  const client = factory(config.apiBaseUrl, config.accessToken);
2203
2239
  const data = await client.request({
2204
2240
  method: "GET",