felo-ai 0.2.46 → 0.2.48
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 +163 -51
- package/felo-livedoc/SKILL.md +6 -0
- package/felo-livedoc/scripts/run_livedoc.mjs +12 -0
- package/felo-slides/SKILL.md +1 -0
- package/felo-slides/scripts/run_ppt_task.mjs +10 -2
- package/felo-superAgent/README.md +223 -280
- package/felo-superAgent/SKILL.md +281 -47
- package/felo-superAgent/scripts/run_style_library.mjs +213 -0
- package/felo-twitter-writer/README.md +117 -13
- package/felo-twitter-writer/SKILL.md +276 -41
- package/package.json +1 -1
- package/src/cli.js +38 -1
- package/src/livedoc.js +23 -0
- package/src/slides.js +7 -2
- package/src/superAgent.js +133 -0
package/src/superAgent.js
CHANGED
|
@@ -789,3 +789,136 @@ export async function superAgent(query, options = {}) {
|
|
|
789
789
|
return 1;
|
|
790
790
|
}
|
|
791
791
|
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Pick the best matching value from a multilingual map object.
|
|
795
|
+
* Falls back: exact match → base language (e.g. "zh" for "zh-Hans") → "en" → first available.
|
|
796
|
+
* Returns an array (may be empty).
|
|
797
|
+
* @param {Object} map - Multilingual map, e.g. { en: [...], "zh-Hans": [...] }
|
|
798
|
+
* @param {string} lang - Preferred language code.
|
|
799
|
+
*/
|
|
800
|
+
function pickLangValue(map, lang) {
|
|
801
|
+
if (!map || typeof map !== 'object') return [];
|
|
802
|
+
if (map[lang]) return map[lang];
|
|
803
|
+
const base = lang.split('-')[0];
|
|
804
|
+
if (base !== lang && map[base]) return map[base];
|
|
805
|
+
if (map['en']) return map['en'];
|
|
806
|
+
const first = Object.values(map)[0];
|
|
807
|
+
return Array.isArray(first) ? first : [];
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Format a single style entry into the brand_style_requirement string.
|
|
812
|
+
* Style name: <name>
|
|
813
|
+
* Style labels: <label1, label2> (from content.labels or content.tags, language-aware)
|
|
814
|
+
* Style DNA: <styleDna> (from content.styleDna, TWITTER type)
|
|
815
|
+
* Cover file ID: <coverFileId> (omitted if null/empty)
|
|
816
|
+
* @param {Object} s - Style entry from API.
|
|
817
|
+
* @param {string} lang - Language code for labels/tags.
|
|
818
|
+
*/
|
|
819
|
+
function formatStyleEntry(s, lang) {
|
|
820
|
+
const lines = [];
|
|
821
|
+
|
|
822
|
+
lines.push(`Style name: ${s.name ?? ''}`);
|
|
823
|
+
|
|
824
|
+
const content = s.content ?? {};
|
|
825
|
+
|
|
826
|
+
// Labels — multilingual map (content.labels for TWITTER, content.tags for others)
|
|
827
|
+
const labelsMap = content.labels ?? content.tags ?? null;
|
|
828
|
+
if (labelsMap) {
|
|
829
|
+
const labelArr = pickLangValue(labelsMap, lang);
|
|
830
|
+
if (labelArr.length > 0) {
|
|
831
|
+
lines.push(`Style labels: ${labelArr.join(', ')}`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Style DNA (TWITTER type)
|
|
836
|
+
if (typeof content.styleDna === 'string' && content.styleDna.trim()) {
|
|
837
|
+
lines.push(`Style DNA: ${content.styleDna.trim()}`);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Cover file ID — omit if null/empty
|
|
841
|
+
const coverId = s.coverFileId ?? s.cover_file_id ?? null;
|
|
842
|
+
if (coverId) {
|
|
843
|
+
lines.push(`Cover file ID: ${coverId}`);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
return lines.join('\n');
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* List style library entries for a given category.
|
|
851
|
+
* @param {string} category - Style category (e.g. TWITTER, INSTAGRAM, LEMON8, NOTECOM, WEBSITE, IMAGE).
|
|
852
|
+
* @param {Object} [options]
|
|
853
|
+
* @param {boolean} [options.json] - Output raw JSON.
|
|
854
|
+
* @param {string} [options.acceptLanguage] - Language code for labels/tags (e.g. en, zh-Hans, ja). Default: en.
|
|
855
|
+
* @param {number} [options.timeoutMs] - Request timeout in ms.
|
|
856
|
+
* @returns {Promise<number>} Exit code 0 or 1.
|
|
857
|
+
*/
|
|
858
|
+
export async function listStyleLibrary(category, options = {}) {
|
|
859
|
+
const apiKey = await getApiKey();
|
|
860
|
+
if (!apiKey) {
|
|
861
|
+
process.stderr.write(NO_KEY_MESSAGE.trim() + '\n');
|
|
862
|
+
return 1;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const apiBase = await getApiBase();
|
|
866
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
867
|
+
const lang = options.acceptLanguage || 'en';
|
|
868
|
+
|
|
869
|
+
const params = new URLSearchParams();
|
|
870
|
+
params.set('category', category);
|
|
871
|
+
|
|
872
|
+
const url = `${apiBase}/v2/brand/style-library/list?${params.toString()}`;
|
|
873
|
+
|
|
874
|
+
const controller = new AbortController();
|
|
875
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
876
|
+
|
|
877
|
+
try {
|
|
878
|
+
const res = await fetch(url, {
|
|
879
|
+
method: 'GET',
|
|
880
|
+
headers: {
|
|
881
|
+
Accept: 'application/json',
|
|
882
|
+
Authorization: `Bearer ${apiKey}`,
|
|
883
|
+
},
|
|
884
|
+
signal: controller.signal,
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
let data = {};
|
|
888
|
+
try {
|
|
889
|
+
data = await res.json();
|
|
890
|
+
} catch {
|
|
891
|
+
data = {};
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (!res.ok) {
|
|
895
|
+
throw new Error(`HTTP ${res.status}: ${getMessage(data)}`);
|
|
896
|
+
}
|
|
897
|
+
if (isApiError(data)) {
|
|
898
|
+
throw new Error(getMessage(data));
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const list = data?.data?.list ?? [];
|
|
902
|
+
|
|
903
|
+
if (options.json) {
|
|
904
|
+
console.log(JSON.stringify(data?.data ?? {}, null, 2));
|
|
905
|
+
} else {
|
|
906
|
+
if (list.length === 0) {
|
|
907
|
+
console.log('(No styles found)');
|
|
908
|
+
} else {
|
|
909
|
+
const userStyles = list.filter((s) => !s.recommended);
|
|
910
|
+
const recommendedStyles = list.filter((s) => s.recommended);
|
|
911
|
+
const allFormatted = [...userStyles, ...recommendedStyles].map((s) => formatStyleEntry(s, lang));
|
|
912
|
+
console.log(allFormatted.join('\n\n'));
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
return 0;
|
|
917
|
+
} catch (err) {
|
|
918
|
+
const msg = err?.message || err;
|
|
919
|
+
process.stderr.write(`Error: ${msg}\n`);
|
|
920
|
+
return 1;
|
|
921
|
+
} finally {
|
|
922
|
+
clearTimeout(timer);
|
|
923
|
+
}
|
|
924
|
+
}
|