credentials.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. const fs = require('fs');
  2. const path = require('path');
  3. const os = require('os');
  4. function readJsonMaybe(filePath) {
  5. try {
  6. if (!filePath || !fs.existsSync(filePath)) return {};
  7. return JSON.parse(fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, ''));
  8. } catch {
  9. return {};
  10. }
  11. }
  12. function firstNonEmpty(values) {
  13. return values.find(value => typeof value === 'string' && value.trim()) || '';
  14. }
  15. function readEnvFileMaybe(filePath) {
  16. try {
  17. if (!filePath || !fs.existsSync(filePath)) return {};
  18. const env = {};
  19. const content = fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '');
  20. for (const rawLine of content.split(/\r?\n/)) {
  21. const line = rawLine.trim();
  22. if (!line || line.startsWith('#')) continue;
  23. const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
  24. if (!match) continue;
  25. let value = match[2].trim();
  26. if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
  27. value = value.slice(1, -1);
  28. }
  29. env[match[1]] = value;
  30. }
  31. return env;
  32. } catch {
  33. return {};
  34. }
  35. }
  36. function readWorkspaceEnvLocal() {
  37. return readEnvFileMaybe(path.join(process.cwd(), '.env.local'));
  38. }
  39. function envLocalCandidates() {
  40. const candidates = [];
  41. let current = process.cwd();
  42. while (current && !candidates.includes(path.join(current, '.env.local'))) {
  43. candidates.push(path.join(current, '.env.local'));
  44. const parent = path.dirname(current);
  45. if (parent === current) break;
  46. current = parent;
  47. }
  48. candidates.push(path.resolve(__dirname, '..', '..', '..', '.env.local'));
  49. return [...new Set(candidates)];
  50. }
  51. function readEnvLocalFiles() {
  52. return envLocalCandidates().reduce((merged, filePath) => {
  53. const next = readEnvFileMaybe(filePath);
  54. for (const [key, value] of Object.entries(next)) {
  55. if (!merged[key]) merged[key] = value;
  56. }
  57. return merged;
  58. }, {});
  59. }
  60. function readVocToken(input = {}) {
  61. const workspaceEnv = readEnvLocalFiles();
  62. const claudeCreds = readJsonMaybe(path.join(os.homedir(), '.claude', 'voc-credentials.json'));
  63. return firstNonEmpty([
  64. input.vocToken,
  65. input.xiaohongshuToken,
  66. input.douyinToken,
  67. input['voc-token'],
  68. input['xiaohongshu-token'],
  69. input['douyin-token'],
  70. input.token,
  71. input.apiToken,
  72. input['api-token'],
  73. workspaceEnv.VOC_TOKEN,
  74. workspaceEnv.VOC_SOCIAL_TOKEN,
  75. process.env.VOC_TOKEN,
  76. process.env.VOC_SOCIAL_TOKEN,
  77. claudeCreds.vocToken,
  78. claudeCreds.token,
  79. ]);
  80. }
  81. function readXiaohongshuToken(input = {}) {
  82. return readVocToken(input);
  83. }
  84. module.exports = {
  85. readVocToken,
  86. readXiaohongshuToken,
  87. readEnvFileMaybe,
  88. readWorkspaceEnvLocal,
  89. envLocalCandidates
  90. };