@telepat/ideon 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.
Files changed (2) hide show
  1. package/dist/ideon.js +107 -15
  2. package/package.json +1 -1
package/dist/ideon.js CHANGED
@@ -56,6 +56,7 @@ var appSettingsSchema = z.object({
56
56
  var envSettingsSchema = z.object({
57
57
  openRouterApiKey: z.string().optional(),
58
58
  replicateApiToken: z.string().optional(),
59
+ disableKeytar: z.boolean().optional(),
59
60
  model: z.string().optional(),
60
61
  temperature: z.number().min(0).max(2).optional(),
61
62
  maxTokens: z.number().int().positive().optional(),
@@ -100,6 +101,7 @@ function readEnvSettings(env = process.env) {
100
101
  return envSettingsSchema.parse({
101
102
  openRouterApiKey: env.IDEON_OPENROUTER_API_KEY,
102
103
  replicateApiToken: env.IDEON_REPLICATE_API_TOKEN,
104
+ disableKeytar: parseBoolean(env.IDEON_DISABLE_KEYTAR),
103
105
  model: env.IDEON_MODEL,
104
106
  temperature: parseNumber(env.IDEON_TEMPERATURE),
105
107
  maxTokens: parseNumber(env.IDEON_MAX_TOKENS),
@@ -1200,17 +1202,80 @@ import keytar from "keytar";
1200
1202
  var SERVICE_NAME = "ideon";
1201
1203
  var OPENROUTER_ACCOUNT = "openrouter-api-key";
1202
1204
  var REPLICATE_ACCOUNT = "replicate-api-token";
1203
- async function loadSecrets() {
1204
- const [openRouterApiKey, replicateApiToken] = await Promise.all([
1205
- keytar.getPassword(SERVICE_NAME, OPENROUTER_ACCOUNT),
1206
- keytar.getPassword(SERVICE_NAME, REPLICATE_ACCOUNT)
1207
- ]);
1205
+ var KEYTAR_UNAVAILABLE_ERROR_NAME = "KeytarUnavailableError";
1206
+ var hasWarnedAboutUnavailableKeytar = false;
1207
+ var KeytarUnavailableError = class extends Error {
1208
+ constructor(message) {
1209
+ super(message);
1210
+ this.name = KEYTAR_UNAVAILABLE_ERROR_NAME;
1211
+ }
1212
+ };
1213
+ function nullSecrets() {
1208
1214
  return {
1209
- openRouterApiKey,
1210
- replicateApiToken
1215
+ openRouterApiKey: null,
1216
+ replicateApiToken: null
1211
1217
  };
1212
1218
  }
1213
- async function saveSecrets(secrets) {
1219
+ function shouldDisableKeytar(options) {
1220
+ return options.disableKeytar === true;
1221
+ }
1222
+ function isKeytarAvailabilityError(error) {
1223
+ if (!(error instanceof Error)) {
1224
+ return false;
1225
+ }
1226
+ const lowered = error.message.toLowerCase();
1227
+ return [
1228
+ "dbus",
1229
+ "d-bus",
1230
+ "org.freedesktop.secrets",
1231
+ "secret service",
1232
+ "secret-service",
1233
+ "keychain",
1234
+ "keyring",
1235
+ "credential store",
1236
+ "credentials were unavailable",
1237
+ "cannot autolaunch",
1238
+ "no such interface",
1239
+ "not supported in this environment"
1240
+ ].some((fragment) => lowered.includes(fragment));
1241
+ }
1242
+ function warnKeytarUnavailable(details) {
1243
+ if (hasWarnedAboutUnavailableKeytar) {
1244
+ return;
1245
+ }
1246
+ hasWarnedAboutUnavailableKeytar = true;
1247
+ console.warn(
1248
+ `System keychain unavailable (${details}). Falling back to environment variables for secrets. Set IDEON_DISABLE_KEYTAR=true to skip keychain access in this environment.`
1249
+ );
1250
+ }
1251
+ async function loadSecrets(options = {}) {
1252
+ if (shouldDisableKeytar(options)) {
1253
+ return nullSecrets();
1254
+ }
1255
+ try {
1256
+ const [openRouterApiKey, replicateApiToken] = await Promise.all([
1257
+ keytar.getPassword(SERVICE_NAME, OPENROUTER_ACCOUNT),
1258
+ keytar.getPassword(SERVICE_NAME, REPLICATE_ACCOUNT)
1259
+ ]);
1260
+ return {
1261
+ openRouterApiKey,
1262
+ replicateApiToken
1263
+ };
1264
+ } catch (error) {
1265
+ if (isKeytarAvailabilityError(error)) {
1266
+ const message = error instanceof Error ? error.message : "unknown error";
1267
+ warnKeytarUnavailable(message);
1268
+ return nullSecrets();
1269
+ }
1270
+ throw error;
1271
+ }
1272
+ }
1273
+ async function saveSecrets(secrets, options = {}) {
1274
+ if (shouldDisableKeytar(options)) {
1275
+ throw new KeytarUnavailableError(
1276
+ "System keychain access is disabled by IDEON_DISABLE_KEYTAR=true. Use IDEON_OPENROUTER_API_KEY and IDEON_REPLICATE_API_TOKEN instead."
1277
+ );
1278
+ }
1214
1279
  const tasks = [];
1215
1280
  if (secrets.openRouterApiKey !== void 0) {
1216
1281
  tasks.push(saveSecretValue(OPENROUTER_ACCOUNT, secrets.openRouterApiKey));
@@ -1221,17 +1286,31 @@ async function saveSecrets(secrets) {
1221
1286
  await Promise.all(tasks);
1222
1287
  }
1223
1288
  async function saveSecretValue(account, value2) {
1224
- if (!value2) {
1225
- await keytar.deletePassword(SERVICE_NAME, account);
1226
- return;
1289
+ try {
1290
+ if (!value2) {
1291
+ await keytar.deletePassword(SERVICE_NAME, account);
1292
+ return;
1293
+ }
1294
+ await keytar.setPassword(SERVICE_NAME, account, value2);
1295
+ } catch (error) {
1296
+ if (isKeytarAvailabilityError(error)) {
1297
+ const message = error instanceof Error ? error.message : "unknown error";
1298
+ throw new KeytarUnavailableError(
1299
+ `System keychain unavailable while saving credentials (${message}). Use IDEON_OPENROUTER_API_KEY and IDEON_REPLICATE_API_TOKEN instead.`
1300
+ );
1301
+ }
1302
+ throw error;
1227
1303
  }
1228
- await keytar.setPassword(SERVICE_NAME, account, value2);
1229
1304
  }
1230
1305
 
1231
1306
  // src/cli/commands/settings.tsx
1232
1307
  import { jsx as jsx2 } from "react/jsx-runtime";
1233
1308
  async function openSettings() {
1234
- const [settings, secrets] = await Promise.all([loadSavedSettings(), loadSecrets()]);
1309
+ const envSettings = readEnvSettings();
1310
+ const [settings, secrets] = await Promise.all([
1311
+ loadSavedSettings(),
1312
+ loadSecrets({ disableKeytar: envSettings.disableKeytar })
1313
+ ]);
1235
1314
  let result = null;
1236
1315
  const app = render(
1237
1316
  /* @__PURE__ */ jsx2(
@@ -1252,7 +1331,17 @@ async function openSettings() {
1252
1331
  return;
1253
1332
  }
1254
1333
  const savedResult = finalResult;
1255
- await Promise.all([saveSettings(savedResult.settings), saveSecrets(savedResult.secrets)]);
1334
+ await saveSettings(savedResult.settings);
1335
+ try {
1336
+ await saveSecrets(savedResult.secrets, { disableKeytar: envSettings.disableKeytar });
1337
+ } catch (error) {
1338
+ if (error instanceof KeytarUnavailableError) {
1339
+ console.log("Settings saved, but secrets were not stored in the system keychain.");
1340
+ console.log("Use IDEON_OPENROUTER_API_KEY and IDEON_REPLICATE_API_TOKEN in this environment.");
1341
+ return;
1342
+ }
1343
+ throw error;
1344
+ }
1256
1345
  console.log(`Settings saved to ${getSettingsFilePath()}.`);
1257
1346
  }
1258
1347
 
@@ -3526,8 +3615,11 @@ import { createInterface } from "readline/promises";
3526
3615
  // src/config/resolver.ts
3527
3616
  import { readFile as readFile4 } from "fs/promises";
3528
3617
  async function resolveRunInput(input) {
3529
- const [savedSettings, secrets] = await Promise.all([loadSavedSettings(), loadSecrets()]);
3530
3618
  const envSettings = readEnvSettings();
3619
+ const [savedSettings, secrets] = await Promise.all([
3620
+ loadSavedSettings(),
3621
+ loadSecrets({ disableKeytar: envSettings.disableKeytar })
3622
+ ]);
3531
3623
  const job = input.jobPath ? await loadJobInput(input.jobPath) : null;
3532
3624
  assertNoLegacyXMode(savedSettings.contentTargets, "saved settings contentTargets");
3533
3625
  assertNoLegacyXMode(job?.settings?.contentTargets, "job settings contentTargets");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telepat/ideon",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "CLI for generating rich articles and images from ideas.",
5
5
  "type": "module",
6
6
  "repository": {