@talesofai/neta-skills 0.16.2 → 0.16.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 (48) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/bin/apis/recsys.js +9 -0
  3. package/bin/commands/adventure_campaign/create_adventure_campaign.cmd.js +1 -4
  4. package/bin/commands/adventure_campaign/list_my_adventure_campaigns.cmd.js +0 -3
  5. package/bin/commands/adventure_campaign/update_adventure_campaign.cmd.js +1 -4
  6. package/bin/commands/character_elementum/list_my_characters.cmd.js +0 -4
  7. package/bin/commands/character_elementum/list_my_elementum.cmd.js +0 -4
  8. package/bin/commands/community/create_comment.cmd.js +2 -1
  9. package/bin/commands/community/favor_collection.cmd.js +2 -1
  10. package/bin/commands/community/get_hashtag_collections.cmd.en_us.yml +0 -3
  11. package/bin/commands/community/get_hashtag_collections.cmd.js +2 -4
  12. package/bin/commands/community/get_hashtag_collections.cmd.zh_cn.yml +0 -2
  13. package/bin/commands/community/like_collection.cmd.js +2 -1
  14. package/bin/commands/community/read_collection.cmd.js +3 -2
  15. package/bin/commands/community/subscribe_user.cmd.js +2 -1
  16. package/bin/commands/creative/edit_colleciton.cmd.js +7 -5
  17. package/bin/commands/creative/publish_collection.cmd.js +6 -4
  18. package/bin/commands/creative/remove_background.cmd.js +3 -2
  19. package/bin/commands/creative/request_character_or_elementum.cmd.en_us.yml +0 -6
  20. package/bin/commands/creative/request_character_or_elementum.cmd.js +6 -12
  21. package/bin/commands/creative/request_character_or_elementum.cmd.zh_cn.yml +0 -6
  22. package/bin/commands/creative/upload.cmd.en_us.yml +0 -4
  23. package/bin/commands/creative/upload.cmd.js +9 -15
  24. package/bin/commands/creative/upload.cmd.zh_cn.yml +0 -4
  25. package/bin/commands/load.js +15 -8
  26. package/bin/commands/premium/create_premium_order.cmd.js +2 -1
  27. package/bin/commands/premium/get_current_premium_plan.cmd.js +2 -4
  28. package/bin/commands/premium/get_premium_order.cmd.js +2 -1
  29. package/bin/commands/premium/list_premium_orders.cmd.js +2 -1
  30. package/bin/commands/premium/list_premium_plans.cmd.js +4 -3
  31. package/bin/commands/premium/pay_premium_order.cmd.js +4 -3
  32. package/bin/commands/user/login.cmd.en_us.yml +1 -4
  33. package/bin/commands/user/login.cmd.js +2 -4
  34. package/bin/commands/user/login.cmd.zh_cn.yml +1 -4
  35. package/bin/utils/auth.js +3 -3
  36. package/bin/utils/env.js +1 -1
  37. package/bin/utils/errors.en_us.yml +25 -0
  38. package/bin/utils/errors.js +29 -0
  39. package/bin/utils/errors.zh_cn.yml +25 -0
  40. package/package.json +1 -1
  41. package/skills/neta/SKILL.md +0 -17
  42. package/skills/neta-adventure/SKILL.md +0 -17
  43. package/skills/neta-character/SKILL.md +0 -17
  44. package/skills/neta-community/SKILL.md +0 -17
  45. package/skills/neta-creative/SKILL.md +0 -17
  46. package/skills/neta-elementum/SKILL.md +0 -17
  47. package/skills/neta-space/SKILL.md +0 -17
  48. package/skills/neta-suggest/SKILL.md +0 -17
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @neta/skills-neta
2
2
 
3
+ ## 0.16.6
4
+
5
+ ### Patch Changes
6
+
7
+ - update read_collection commands api
8
+ - add user check for all commands exclude "login"
9
+ - move inline error text to utils/errors.ts file
10
+ - remove authorization from SKILL.md, add login usage to not authorization error and login -h command
11
+
12
+ ## 0.16.5
13
+
14
+ ### Patch Changes
15
+
16
+ - update auth api base url
17
+
18
+ ## 0.16.4
19
+
20
+ ### Patch Changes
21
+
22
+ - fix build
23
+
24
+ ## 0.16.3
25
+
26
+ ### Patch Changes
27
+
28
+ - update auth api resource
29
+
3
30
  ## 0.16.2
4
31
 
5
32
  ### Patch Changes
@@ -19,11 +19,20 @@ export const createRecsysApis = (client) => {
19
19
  const response = await client.post("/v1/recsys/validate-tax-path", params);
20
20
  return response.data;
21
21
  };
22
+ const detail = async (uuid) => {
23
+ const response = await client.get("/v1/recsys/interactive/details", {
24
+ params: {
25
+ uuid,
26
+ },
27
+ });
28
+ return response.data.module_list[0];
29
+ };
22
30
  return {
23
31
  suggestKeywords,
24
32
  suggestTags,
25
33
  suggestCategories,
26
34
  suggestContent,
27
35
  validateTaxPath,
36
+ detail,
28
37
  };
29
38
  };
@@ -39,10 +39,7 @@ export const createAdventureCampaign = createCommand({
39
39
  title: meta.title,
40
40
  description: meta.description,
41
41
  inputSchema: createAdventureCampaignParameters,
42
- }, async ({ name, mission_plot, subtitle, status, header_img, background_img, mission_task, mission_plot_attention, }, { user, apis }) => {
43
- if (!user) {
44
- throw new Error("Not authenticated. Please check your NETA_TOKEN or login.");
45
- }
42
+ }, async ({ name, mission_plot, subtitle, status, header_img, background_img, mission_task, mission_plot_attention, }, { apis }) => {
46
43
  const result = await apis.travelCampaign.createCampaign({
47
44
  name,
48
45
  mission_plot,
@@ -29,9 +29,6 @@ export const listMyAdventureCampaigns = createCommand({
29
29
  description: meta.description,
30
30
  inputSchema: listMyAdventureCampaignsParameters,
31
31
  }, async ({ page_index, page_size }, { user, apis }) => {
32
- if (!user) {
33
- throw new Error("Not authenticated. Please check your NETA_TOKEN or login.");
34
- }
35
32
  const result = await apis.travelCampaign.listMyCampaigns({
36
33
  user_uuid: user.uuid,
37
34
  page_index,
@@ -40,10 +40,7 @@ export const updateAdventureCampaign = createCommand({
40
40
  title: meta.title,
41
41
  description: meta.description,
42
42
  inputSchema: updateAdventureCampaignParameters,
43
- }, async ({ campaign_uuid, name, mission_plot, subtitle, status, header_img, background_img, mission_task, mission_plot_attention, }, { user, apis }) => {
44
- if (!user) {
45
- throw new Error("Not authenticated. Please check your NETA_TOKEN or login.");
46
- }
43
+ }, async ({ campaign_uuid, name, mission_plot, subtitle, status, header_img, background_img, mission_task, mission_plot_attention, }, { apis }) => {
47
44
  const patch = Object.fromEntries(Object.entries({
48
45
  name,
49
46
  mission_plot,
@@ -17,10 +17,6 @@ export const listMyCharacters = createCommand({
17
17
  description: meta.description,
18
18
  inputSchema: listMyCharactersParameters,
19
19
  }, async ({ keyword, page_index, page_size }, { apis, user }) => {
20
- // Get current user info to obtain UUID
21
- if (!user) {
22
- throw new Error("Failed to get user info. Please check your NETA_TOKEN.");
23
- }
24
20
  const result = await apis.tcp.listMyTCPs({
25
21
  user_uuid: user.uuid,
26
22
  parent_type: "oc",
@@ -17,10 +17,6 @@ export const listMyElementum = createCommand({
17
17
  description: meta.description,
18
18
  inputSchema: listMyElementumParameters,
19
19
  }, async ({ keyword, page_index, page_size }, { apis, user }) => {
20
- // Get current user info to obtain UUID
21
- if (!user) {
22
- throw new Error("Failed to get user info. Please check your NETA_TOKEN.");
23
- }
24
20
  const result = await apis.tcp.listMyTCPs({
25
21
  user_uuid: user.uuid,
26
22
  parent_type: "elementum",
@@ -1,4 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { createCommand } from "../factory.js";
4
5
  const meta = parseMeta(Type.Object({
@@ -30,7 +31,7 @@ export const createCommentCmd = createCommand({
30
31
  at_users: atUsersArray,
31
32
  });
32
33
  if (!result.success) {
33
- throw new Error("create_comment fail");
34
+ throw new Error(errors.create_comment_fail);
34
35
  }
35
36
  return {
36
37
  success: true,
@@ -1,4 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { createCommand } from "../factory.js";
4
5
  const meta = parseMeta(Type.Object({
@@ -20,7 +21,7 @@ export const favorCollectionCmd = createCommand({
20
21
  const action = is_cancel ? "cancel_favor" : "favor";
21
22
  const result = await apis.collection.favorCollection(uuid, { is_cancel });
22
23
  if (!result.success) {
23
- throw new Error(`${action} fail`);
24
+ throw new Error(errors.action_fail.replace("{action}", action));
24
25
  }
25
26
  return {
26
27
  success: true,
@@ -1,6 +1,3 @@
1
1
  name: get_hashtag_collections
2
2
  title: Get Featured Collections Under Hashtag
3
3
  description: Retrieve featured collections associated with a given hashtag.
4
- errors:
5
- no_activity_space: Hashtag "{hashtag}" is not linked to any activity space.
6
-
@@ -1,13 +1,11 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { createCommand } from "../factory.js";
4
5
  const meta = parseMeta(Type.Object({
5
6
  name: Type.String(),
6
7
  title: Type.String(),
7
8
  description: Type.String(),
8
- errors: Type.Object({
9
- no_activity_space: Type.String(),
10
- }),
11
9
  }), import.meta);
12
10
  const fetchSelectedCollectionsByHashtagV1Parameters = Type.Object({
13
11
  hashtag: Type.String(),
@@ -28,7 +26,7 @@ export const getHashtagCollections = createCommand({
28
26
  const activityDetail = hashtagResult?.activity_detail;
29
27
  const activity_uuid = activityDetail?.uuid;
30
28
  if (!activity_uuid) {
31
- throw new Error(meta.errors.no_activity_space.replace("{hashtag}", String(hashtag ?? "").trim()));
29
+ throw new Error(errors.hashtag_not_linked_to_activity_space.replace("{hashtag}", String(hashtag ?? "").trim()));
32
30
  }
33
31
  const result = await apis.activity.fetchSelectedCollections(activity_uuid, {
34
32
  page_index,
@@ -1,5 +1,3 @@
1
1
  name: get_hashtag_collections
2
2
  title: 获取hashtag下的精选玩法
3
3
  description: 获取hashtag下的精选玩法
4
- errors:
5
- no_activity_space: Hashtag "{hashtag}" 未关联任何活动空间
@@ -1,4 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { createCommand } from "../factory.js";
4
5
  const meta = parseMeta(Type.Object({
@@ -20,7 +21,7 @@ export const likeCollectionCmd = createCommand({
20
21
  const action = is_cancel ? "unliked" : "liked";
21
22
  const success = await apis.collection.likeCollection(uuid, { is_cancel });
22
23
  if (!success) {
23
- throw new Error(`${action} fail`);
24
+ throw new Error(errors.action_fail.replace("{action}", action));
24
25
  }
25
26
  return {
26
27
  success: true,
@@ -1,5 +1,6 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { isVerseCTA } from "../../apis/collection.js";
3
+ import { errors } from "../../utils/errors.js";
3
4
  import { parseMeta } from "../../utils/parse_meta.js";
4
5
  import { createCommand } from "../factory.js";
5
6
  const mete = parseMeta(Type.Object({
@@ -15,9 +16,9 @@ export const readCollectionCmd = createCommand({
15
16
  uuid: Type.String(),
16
17
  }),
17
18
  }, async ({ uuid }, { apis }) => {
18
- const res = await apis.feeds.interactiveItem({ collection_uuid: uuid });
19
+ const res = await apis.recsys.detail(uuid);
19
20
  if (!res || res.template_id !== "NORMAL") {
20
- throw new Error(`Collection "${uuid}" not found`);
21
+ throw new Error(errors.collection_not_found.replace("{uuid}", uuid));
21
22
  }
22
23
  const name = res.json_data.name;
23
24
  const description = res.json_data.description ?? "";
@@ -1,4 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { createCommand } from "../factory.js";
4
5
  const meta = parseMeta(Type.Object({
@@ -23,7 +24,7 @@ export const subscribeUserCmd = createCommand({
23
24
  is_cancel,
24
25
  });
25
26
  if (!result.success) {
26
- throw new Error(`${action} fail`);
27
+ throw new Error(errors.action_fail.replace("{action}", action));
27
28
  }
28
29
  return {
29
30
  success: true,
@@ -1,4 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { createCommand } from "../factory.js";
4
5
  const meta = parseMeta(Type.Object({
@@ -32,7 +33,7 @@ export const editCollection = createCommand({
32
33
  }, async ({ uuid, name, description, status, hashtags, artifacts, remix_instruct }, { apis }) => {
33
34
  const [collection] = await apis.collection.collectionDetails([uuid]);
34
35
  if (!collection) {
35
- throw new Error(`collection ${uuid} not found`);
36
+ throw new Error(errors.creative_collection_not_found.replace("{uuid}", uuid));
36
37
  }
37
38
  const payload = {
38
39
  uuid,
@@ -70,16 +71,17 @@ export const editCollection = createCommand({
70
71
  payload.activity = activity?.activity_detail?.uuid ?? null;
71
72
  }
72
73
  if (artifacts) {
73
- const artifactDetails = await apis.artifact.artifactDetail(artifacts?.split(",") ?? []);
74
+ const artifactIds = artifacts.split(",");
75
+ const artifactDetails = await apis.artifact.artifactDetail(artifactIds);
74
76
  if (artifactDetails.length < 1 || artifactDetails.length > 12) {
75
- throw new Error("artifacts must be between 1 and 12");
77
+ throw new Error(errors.collection_artifact_count_range);
76
78
  }
77
79
  artifactDetails.forEach((artifact, index) => {
78
80
  if (!artifact) {
79
- throw new Error(`artifact ${artifacts[index]} not found`);
81
+ throw new Error(errors.collection_artifact_not_found.replace("{artifact_uuid}", artifactIds[index] ?? ""));
80
82
  }
81
83
  if (artifact.status !== "SUCCESS") {
82
- throw new Error(`artifact ${artifacts[index]} status is not success`);
84
+ throw new Error(errors.collection_artifact_not_success.replace("{artifact_uuid}", artifactIds[index] ?? ""));
83
85
  }
84
86
  });
85
87
  const coverArtifact = artifactDetails.find((artifact) => artifact.url) ?? null;
@@ -1,4 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { createCommand } from "../factory.js";
4
5
  const meta = parseMeta(Type.Object({
@@ -31,16 +32,17 @@ export const publishCollection = createCommand({
31
32
  const tags = await Promise.all(hashtags?.split(",")?.map((tag) => apis.hashtag.createHashtag(tag)) ?? []);
32
33
  const tagDetails = await Promise.all(tags.map((tag) => apis.hashtag.fetchHashtag(tag.name)));
33
34
  const activity = tagDetails.find((tag) => tag?.activity_detail?.uuid) ?? null;
34
- const artifactDetails = await apis.artifact.artifactDetail(artifacts.split(","));
35
+ const artifactIds = artifacts.split(",");
36
+ const artifactDetails = await apis.artifact.artifactDetail(artifactIds);
35
37
  if (artifactDetails.length < 1 || artifactDetails.length > 12) {
36
- throw new Error("artifacts must be between 1 and 12");
38
+ throw new Error(errors.collection_artifact_count_range);
37
39
  }
38
40
  artifactDetails.forEach((artifact, index) => {
39
41
  if (!artifact) {
40
- throw new Error(`artifact ${artifacts[index]} not found`);
42
+ throw new Error(errors.collection_artifact_not_found.replace("{artifact_uuid}", artifactIds[index] ?? ""));
41
43
  }
42
44
  if (artifact.status !== "SUCCESS") {
43
- throw new Error(`artifact ${artifacts[index]} status is not success`);
45
+ throw new Error(errors.collection_artifact_not_success.replace("{artifact_uuid}", artifactIds[index] ?? ""));
44
46
  }
45
47
  });
46
48
  const uuid = await apis.collection.createCollection();
@@ -1,4 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { polling } from "../../utils/polling.js";
4
5
  import { createCommand } from "../factory.js";
@@ -34,7 +35,7 @@ export const removeBackground = createCommand({
34
35
  !artifacts[0] ||
35
36
  artifacts[0].modality !== "PICTURE" ||
36
37
  artifacts[0].status !== "SUCCESS") {
37
- throw new Error("Input is not a valid picture artifact UUID");
38
+ throw new Error(errors.invalid_picture_artifact_uuid);
38
39
  }
39
40
  return apis.artifact.postProcess(input_image, "0_null/抠图SEG", {
40
41
  entrance: "PICTURE,CLI",
@@ -69,7 +70,7 @@ export const removeBackgroundNoCrop = createCommand({
69
70
  !artifacts[0] ||
70
71
  artifacts[0].modality !== "PICTURE" ||
71
72
  artifacts[0].status !== "SUCCESS") {
72
- throw new Error("Input is not a valid picture artifact UUID");
73
+ throw new Error(errors.invalid_picture_artifact_uuid);
73
74
  }
74
75
  return apis.artifact.postProcess(input_image, "0_null/抠图SEG", {
75
76
  entrance: "PICTURE,CLI",
@@ -1,12 +1,6 @@
1
1
  name: request_character_or_elementum
2
2
  title: Get Character or Elementum
3
3
  description: Fetch detailed information about a character or elementum by exact name or UUID.
4
- errors:
5
- missing_id: Either name or uuid must be provided.
6
- not_found: Character or elementum not found {identifier}
7
- type_mismatch_character: The resolved type is "character", but parent_type="elementum" was specified. Please adjust parent_type or use a different name to search.
8
- type_mismatch_elementum: The resolved type is "elementum", but parent_type="character" was specified. Please adjust parent_type or use a different name to search.
9
- unknown_type: Unknown TCP type {type}
10
4
  parameters:
11
5
  name: |
12
6
  Exact name of the character or elementum.
@@ -1,17 +1,11 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { errors } from "../../utils/errors.js";
2
3
  import { parseMeta } from "../../utils/parse_meta.js";
3
4
  import { createCommand } from "../factory.js";
4
5
  const meta = parseMeta(Type.Object({
5
6
  name: Type.String(),
6
7
  title: Type.String(),
7
8
  description: Type.String(),
8
- errors: Type.Object({
9
- missing_id: Type.String(),
10
- not_found: Type.String(),
11
- type_mismatch_character: Type.String(),
12
- type_mismatch_elementum: Type.String(),
13
- unknown_type: Type.String(),
14
- }),
15
9
  parameters: Type.Object({
16
10
  name: Type.String(),
17
11
  uuid: Type.String(),
@@ -36,7 +30,7 @@ export const requestCharacterOrElementum = createCommand({
36
30
  const targetType = parent_type === "both" ? ["character", "elementum"] : [parent_type];
37
31
  const getTcp = async () => {
38
32
  if (!uuid && !name) {
39
- throw new Error(meta.errors.missing_id);
33
+ throw new Error(errors.tcp_missing_id_or_name);
40
34
  }
41
35
  if (uuid) {
42
36
  return await apis.tcp.tcpProfile(uuid);
@@ -66,11 +60,11 @@ export const requestCharacterOrElementum = createCommand({
66
60
  };
67
61
  const tcp = await getTcp();
68
62
  if (!tcp) {
69
- throw new Error(meta.errors.not_found.replace("{identifier}", String((name || uuid) ?? "").trim()));
63
+ throw new Error(errors.tcp_not_found.replace("{identifier}", String((name || uuid) ?? "").trim()));
70
64
  }
71
65
  if (tcp.type === "oc" || tcp.type === "official") {
72
66
  if (!targetType.includes("character")) {
73
- throw new Error(meta.errors.type_mismatch_character);
67
+ throw new Error(errors.tcp_type_mismatch_character);
74
68
  }
75
69
  const assignValue = {
76
70
  type: "character",
@@ -90,7 +84,7 @@ export const requestCharacterOrElementum = createCommand({
90
84
  }
91
85
  if (tcp.type === "elementum") {
92
86
  if (!targetType.includes("elementum")) {
93
- throw new Error(meta.errors.type_mismatch_elementum);
87
+ throw new Error(errors.tcp_type_mismatch_elementum);
94
88
  }
95
89
  const assignValue = {
96
90
  type: "elementum",
@@ -104,5 +98,5 @@ export const requestCharacterOrElementum = createCommand({
104
98
  };
105
99
  }
106
100
  log.warn(`request_character_or_elementum: unknown tcp type: ${tcp.type}`);
107
- throw new Error(meta.errors.unknown_type.replace("{type}", String(tcp.type)));
101
+ throw new Error(errors.tcp_unknown_type.replace("{type}", String(tcp.type)));
108
102
  });
@@ -1,12 +1,6 @@
1
1
  name: request_character_or_elementum
2
2
  title: 请求角色或元素
3
3
  description: 通过名称(精确匹配)或UUID获取角色或元素的详细信息
4
- errors:
5
- missing_id: 必须提供 name 或 uuid 参数之一
6
- not_found: 未找到角色或元素 {identifier}
7
- type_mismatch_character: 找到的类型为 "character"(角色),但指定了 parent_type="elementum"(风格元素)。请调整 parent_type 参数或使用不同的名称搜索。
8
- type_mismatch_elementum: 找到的类型为 "elementum"(风格元素),但指定了 parent_type="character"(角色)。请调整 parent_type 参数或使用不同的名称搜索。
9
- unknown_type: 未知 TCP 类型 {type}
10
4
  parameters:
11
5
  name: |
12
6
  角色或元素的名称(精确匹配)
@@ -4,7 +4,3 @@ description: Upload a media file (image or video) to create a media artifact.
4
4
 
5
5
  parameters:
6
6
  file_path: Local media path (absolute or relative to cwd), or http(s) URL to fetch the file
7
-
8
- errors:
9
- file_type_not_supported: Media file type not supported
10
- file_size_too_large: Media file size too large, maximum size is {max_size} bytes
@@ -3,6 +3,7 @@ import { CompleteMultipartUploadCommand, CreateMultipartUploadCommand, S3Client,
3
3
  import { Type } from "@sinclair/typebox";
4
4
  import { filetypeinfo } from "magic-bytes.js";
5
5
  import plimit from "p-limit";
6
+ import { errors } from "../../utils/errors.js";
6
7
  import { parseMeta } from "../../utils/parse_meta.js";
7
8
  import { polling } from "../../utils/polling.js";
8
9
  import { createCommand } from "../factory.js";
@@ -39,10 +40,6 @@ const meta = parseMeta(Type.Object({
39
40
  parameters: Type.Object({
40
41
  file_path: Type.String(),
41
42
  }),
42
- errors: Type.Object({
43
- file_type_not_supported: Type.String(),
44
- file_size_too_large: Type.String(),
45
- }),
46
43
  }), import.meta);
47
44
  const s3Upload = async (data, options) => {
48
45
  const { mimeType, regionOptions, credentials, logger } = options;
@@ -51,7 +48,7 @@ const s3Upload = async (data, options) => {
51
48
  const now = Date.now();
52
49
  const expires = new Date(expiration).getTime();
53
50
  if (now > expires) {
54
- throw new Error("STS token expired");
51
+ throw new Error(errors.sts_token_expired);
55
52
  }
56
53
  const client = new S3Client({
57
54
  region,
@@ -124,12 +121,12 @@ const createArtifact = async (url, options) => {
124
121
  const res = await polling(() => apis.artifact.artifactDetail([uuid]), (result) => {
125
122
  const artifact = result[0];
126
123
  if (!artifact)
127
- throw new Error("Artifact not found");
124
+ throw new Error(errors.artifact_not_found);
128
125
  logger.debug("polling: %o", artifact);
129
126
  return artifact.status !== "PENDING" && artifact.status !== "MODERATION";
130
127
  }, 1000, 60 * 1000);
131
128
  if (res.isTimeout) {
132
- throw new Error("Timeout");
129
+ throw new Error(errors.operation_timeout);
133
130
  }
134
131
  // biome-ignore lint/style/noNonNullAssertion: checked
135
132
  return res.result[0];
@@ -144,10 +141,7 @@ export const upload = createCommand({
144
141
  inputSchema: Type.Object({
145
142
  file_path: Type.String({ description: meta.parameters.file_path }),
146
143
  }),
147
- }, async ({ file_path }, { apis, user, log }) => {
148
- if (!user) {
149
- throw new Error("Not authenticated. Please check your NETA_TOKEN or login.");
150
- }
144
+ }, async ({ file_path }, { apis, log }) => {
151
145
  const regionOptions = apis.baseUrl.endsWith(".cn")
152
146
  ? OSS_STS_OPTIONS_CN
153
147
  : OSS_STS_OPTIONS_US;
@@ -159,11 +153,11 @@ export const upload = createCommand({
159
153
  const infos = filetypeinfo(file);
160
154
  const info = infos[0]; // always use first extension
161
155
  if (!info || !info.extension || !info.mime) {
162
- throw new Error(meta.errors.file_type_not_supported);
156
+ throw new Error(errors.upload_file_type_not_supported);
163
157
  }
164
158
  if (SUPPORTED_IMAGE_TYPES.includes(info.extension)) {
165
159
  if (file.length > DEFAULT_IMAGE_LIMIT_SIZE) {
166
- throw new Error(meta.errors.file_size_too_large.replace("{max_size}", DEFAULT_IMAGE_LIMIT_SIZE.toString()));
160
+ throw new Error(errors.upload_file_size_too_large.replace("{max_size}", DEFAULT_IMAGE_LIMIT_SIZE.toString()));
167
161
  }
168
162
  const credentials = await apis.oss.getStsCredentials(info.extension);
169
163
  const url = await s3Upload(file, {
@@ -180,7 +174,7 @@ export const upload = createCommand({
180
174
  }
181
175
  if (SUPPORTED_VIDEO_TYPES.includes(info.extension)) {
182
176
  if (file.length > DEFAULT_VIDEO_LIMIT_SIZE) {
183
- throw new Error(meta.errors.file_size_too_large.replace("{max_size}", DEFAULT_VIDEO_LIMIT_SIZE.toString()));
177
+ throw new Error(errors.upload_file_size_too_large.replace("{max_size}", DEFAULT_VIDEO_LIMIT_SIZE.toString()));
184
178
  }
185
179
  const credentials = await apis.oss.getVideoStsCredentials(info.extension);
186
180
  const url = await s3Upload(file, {
@@ -195,5 +189,5 @@ export const upload = createCommand({
195
189
  logger: log,
196
190
  });
197
191
  }
198
- throw new Error(meta.errors.file_type_not_supported);
192
+ throw new Error(errors.upload_file_type_not_supported);
199
193
  });
@@ -4,7 +4,3 @@ description: 上传媒体文件(图片、视频)并创建一个媒体素材
4
4
 
5
5
  parameters:
6
6
  file_path: 本地媒体路径(绝对路径或相对于当前工作目录),或用于拉取文件的 http(s) 网址
7
-
8
- errors:
9
- file_type_not_supported: 文件类型不支持
10
- file_size_too_large: 文件大小超过最大限制, 最大支持 {max_size} 字节
@@ -14,7 +14,7 @@ import { Type } from "@sinclair/typebox";
14
14
  import { AssertError, Value } from "@sinclair/typebox/value";
15
15
  import { createApis } from "../apis/index.js";
16
16
  import { API_BASE_URL, IS_DEV } from "../utils/env.js";
17
- import { ApiResponseError } from "../utils/errors.js";
17
+ import { ApiResponseError, errors } from "../utils/errors.js";
18
18
  import { getSysLocale } from "../utils/lang.js";
19
19
  import { logger } from "../utils/logger.js";
20
20
  import { setLocale } from "../utils/parse_meta.js";
@@ -52,7 +52,8 @@ export const buildCommands = async (cli) => {
52
52
  ]);
53
53
  commands.forEach((cmd) => {
54
54
  const command = cli.command(cmd.name);
55
- command.description(cmd.title || cmd.description || "");
55
+ command.description(cmd.description || "");
56
+ command.summary(cmd.title || "");
56
57
  const inputSchema = cmd.inputSchema;
57
58
  if (inputSchema && "properties" in inputSchema) {
58
59
  const properties = inputSchema.properties;
@@ -99,17 +100,22 @@ export const buildCommands = async (cli) => {
99
100
  "x-platform": "nieta-app/web",
100
101
  },
101
102
  });
102
- const user = cmd.name === "login" || cmd.name === "logout"
103
+ const user = cmd.name === "login"
103
104
  ? null
104
105
  : await apis.user.me().catch((e) => {
105
106
  if (e instanceof ApiResponseError) {
106
- return null;
107
+ logger.info(`${e.name}[${e.code}]: ${e.message}`);
107
108
  }
108
- return null;
109
+ else {
110
+ logger.warn(e);
111
+ }
112
+ throw new Error(errors.need_login);
109
113
  });
110
114
  const startTime = Date.now();
111
- logger.debug("[telemetry] user: %s", user?.uuid);
112
- trackConfigUser(user ? { user_unique_id: user.uuid } : null);
115
+ if (user) {
116
+ logger.debug("[telemetry] user: %s", user.uuid);
117
+ trackConfigUser({ user_unique_id: user.uuid });
118
+ }
113
119
  track("command_call", {
114
120
  command: cmd.name,
115
121
  ...formatCommandParams(args),
@@ -120,7 +126,8 @@ export const buildCommands = async (cli) => {
120
126
  await cmd
121
127
  .execute(input, {
122
128
  apis,
123
- user,
129
+ // biome-ignore lint/style/noNonNullAssertion: ignore type error when user is null by login command
130
+ user: user,
124
131
  log: logger,
125
132
  })
126
133
  .then((result) => {
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { parseDate } from "../../utils/date.js";
3
3
  import { IS_GLOBAL } from "../../utils/env.js";
4
+ import { errors } from "../../utils/errors.js";
4
5
  import { parseMeta } from "../../utils/parse_meta.js";
5
6
  import { createCommand } from "../factory.js";
6
7
  const meta = parseMeta(Type.Object({
@@ -20,7 +21,7 @@ export const createPremiumOrder = createCommand({
20
21
  }),
21
22
  }, async ({ spu_uuid }, { apis }) => {
22
23
  if (!IS_GLOBAL) {
23
- throw new Error("This command is not supported in the current region");
24
+ throw new Error(errors.not_supported_in_current_region);
24
25
  }
25
26
  const order = await apis.commerce.createOrder({
26
27
  spu_uuid,
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { parseDate } from "../../utils/date.js";
3
3
  import { IS_GLOBAL } from "../../utils/env.js";
4
+ import { errors } from "../../utils/errors.js";
4
5
  import { parseMeta } from "../../utils/parse_meta.js";
5
6
  import { createCommand } from "../factory.js";
6
7
  const PlanningMap = {
@@ -20,10 +21,7 @@ export const getCurrentPremiumPlan = createCommand({
20
21
  description: meta.description,
21
22
  }, async (_, { user }) => {
22
23
  if (!IS_GLOBAL) {
23
- throw new Error("This command is not supported in the current region");
24
- }
25
- if (!user) {
26
- throw new Error("Not authenticated. Please check your NETA_TOKEN or login.");
24
+ throw new Error(errors.not_supported_in_current_region);
27
25
  }
28
26
  const level = user.properties?.vip_level ?? 0;
29
27
  return {
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { parseDate } from "../../utils/date.js";
3
3
  import { IS_GLOBAL } from "../../utils/env.js";
4
+ import { errors } from "../../utils/errors.js";
4
5
  import { parseMeta } from "../../utils/parse_meta.js";
5
6
  import { createCommand } from "../factory.js";
6
7
  const meta = parseMeta(Type.Object({
@@ -20,7 +21,7 @@ export const getPremiumOrder = createCommand({
20
21
  }),
21
22
  }, async ({ order_uuid }, { apis }) => {
22
23
  if (!IS_GLOBAL) {
23
- throw new Error("This command is not supported in the current region");
24
+ throw new Error(errors.not_supported_in_current_region);
24
25
  }
25
26
  const order = await apis.commerce.order({ order_uuid });
26
27
  return {
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { parseDate } from "../../utils/date.js";
3
3
  import { IS_GLOBAL } from "../../utils/env.js";
4
+ import { errors } from "../../utils/errors.js";
4
5
  import { parseMeta } from "../../utils/parse_meta.js";
5
6
  import { createCommand } from "../factory.js";
6
7
  const meta = parseMeta(Type.Object({
@@ -31,7 +32,7 @@ export const listPremiumOrders = createCommand({
31
32
  }),
32
33
  }, async ({ page_index, page_size }, { apis }) => {
33
34
  if (!IS_GLOBAL) {
34
- throw new Error("This command is not supported in the current region");
35
+ throw new Error(errors.not_supported_in_current_region);
35
36
  }
36
37
  const { list, total } = await apis.commerce.orders({
37
38
  page_index,
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { Value } from "@sinclair/typebox/value";
3
3
  import { IS_GLOBAL } from "../../utils/env.js";
4
+ import { errors } from "../../utils/errors.js";
4
5
  import { safeParseJson } from "../../utils/json.js";
5
6
  import { parseMeta } from "../../utils/parse_meta.js";
6
7
  import { createCommand } from "../factory.js";
@@ -46,11 +47,11 @@ export const listPremiumPlans = createCommand({
46
47
  description: meta.description,
47
48
  }, async (_, { apis }) => {
48
49
  if (!IS_GLOBAL) {
49
- throw new Error("This command is not supported in the current region");
50
+ throw new Error(errors.not_supported_in_current_region);
50
51
  }
51
52
  const plansConfigValue = await apis.commerce.listPlansConfig();
52
53
  if (!plansConfigValue || plansConfigValue.type !== "json") {
53
- throw new Error("Premium subscription plans config not found");
54
+ throw new Error(errors.premium_plans_config_not_found);
54
55
  }
55
56
  const json = safeParseJson(plansConfigValue.value);
56
57
  try {
@@ -61,7 +62,7 @@ export const listPremiumPlans = createCommand({
61
62
  };
62
63
  }
63
64
  catch (error) {
64
- throw new Error("Premium subscription plans config is invalid", {
65
+ throw new Error(errors.premium_plans_config_invalid, {
65
66
  cause: error,
66
67
  });
67
68
  }
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { parseDate } from "../../utils/date.js";
3
3
  import { IS_GLOBAL } from "../../utils/env.js";
4
+ import { errors } from "../../utils/errors.js";
4
5
  import { parseMeta } from "../../utils/parse_meta.js";
5
6
  import { createCommand } from "../factory.js";
6
7
  const meta = parseMeta(Type.Object({
@@ -24,14 +25,14 @@ export const payPremiumOrder = createCommand({
24
25
  }),
25
26
  }, async ({ order_uuid, channel }, { apis }) => {
26
27
  if (!IS_GLOBAL) {
27
- throw new Error("This command is not supported in the current region");
28
+ throw new Error(errors.not_supported_in_current_region);
28
29
  }
29
30
  const order = await apis.commerce.order({ order_uuid });
30
31
  if (order.status !== "UNPAID") {
31
- throw new Error("Order is not unpaid");
32
+ throw new Error(errors.order_not_unpaid);
32
33
  }
33
34
  if (parseDate(order.valid_until).isBefore(Date.now())) {
34
- throw new Error("Order is expired");
35
+ throw new Error(errors.order_expired);
35
36
  }
36
37
  const result = await apis.commerce.pay({
37
38
  order_uuid,
@@ -1,8 +1,5 @@
1
1
  name: login
2
2
  title: Login
3
- description: Login to your Neta account
3
+ description: Login to your Neta account by OAuth2 device code flow. 1. Run command "login --action request-code" to request a device code. 2. Open the browser and visit the URL provided by the command to verify. 3. Run command "login --action verify-code" to verify the device code and complete the login.
4
4
  parameters:
5
5
  action: Action to perform (request-code or verify-code)
6
-
7
- errors:
8
- not_supported_in_current_region: This command is not supported in the current region, please set NETA_TOKEN environment variable for authentication.
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { requestDeviceCode, verifyDeviceCode } from "../../utils/auth.js";
3
3
  import { IS_GLOBAL } from "../../utils/env.js";
4
+ import { errors } from "../../utils/errors.js";
4
5
  import { parseMeta } from "../../utils/parse_meta.js";
5
6
  import { createCommand } from "../factory.js";
6
7
  export const meta = parseMeta(Type.Object({
@@ -10,9 +11,6 @@ export const meta = parseMeta(Type.Object({
10
11
  parameters: Type.Object({
11
12
  action: Type.String(),
12
13
  }),
13
- errors: Type.Object({
14
- not_supported_in_current_region: Type.String(),
15
- }),
16
14
  }), import.meta);
17
15
  export const login = createCommand({
18
16
  name: meta.name,
@@ -26,7 +24,7 @@ export const login = createCommand({
26
24
  }),
27
25
  }, async ({ action }, { apis }) => {
28
26
  if (!IS_GLOBAL) {
29
- throw new Error(meta.errors.not_supported_in_current_region);
27
+ throw new Error(errors.not_supported_in_current_region);
30
28
  }
31
29
  if (action === "verify-code") {
32
30
  await verifyDeviceCode();
@@ -1,8 +1,5 @@
1
1
  name: login
2
2
  title: 登录
3
- description: 登录到你的 Neta 账户
3
+ description: 通过 OAuth2 设备授权码流登录到你的 Neta 账户。 1. 运行命令 "login --action request-code" 请求验证码。 2. 打开浏览器并访问命令提供的 URL 完成校验。 3. 运行命令 "login --action verify-code" 验证验证码并完成登录流程。
4
4
  parameters:
5
5
  action: 要执行的操作 (request-code 请求验证码, verify-code 验证验证码)
6
-
7
- errors:
8
- not_supported_in_current_region: 当前区域不支持此命令, 请设置 NETA_TOKEN 环境变量进行身份验证
package/bin/utils/auth.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { Value } from "@sinclair/typebox/value";
3
3
  import { deleteConfig, readConfig, writeConfig } from "./config.js";
4
- import { API_BASE_URL, AUTH_API_BASE_URL, CLIENT_ID } from "./env.js";
4
+ import { AUTH_API_BASE_URL, CLIENT_ID } from "./env.js";
5
5
  import { ApiResponseError } from "./errors.js";
6
6
  import { safeParseJson } from "./json.js";
7
7
  import { logger } from "./logger.js";
@@ -121,7 +121,7 @@ export const requestDeviceCode = async () => {
121
121
  body: new URLSearchParams({
122
122
  client_id: CLIENT_ID,
123
123
  scope: "profile email offline_access",
124
- resource: API_BASE_URL,
124
+ resource: "https://api.talesofai",
125
125
  }),
126
126
  });
127
127
  if (!res.ok) {
@@ -168,7 +168,7 @@ export const refreshToken = async () => {
168
168
  grant_type: "refresh_token",
169
169
  refresh_token: tokenConfig.refresh_token,
170
170
  scope: "profile email offline_access",
171
- resource: API_BASE_URL,
171
+ resource: "https://api.talesofai",
172
172
  }),
173
173
  });
174
174
  if (!res.ok) {
package/bin/utils/env.js CHANGED
@@ -5,7 +5,7 @@ dotenv.config({
5
5
  });
6
6
  export const IS_DEV = process.env["NODE_ENV"] === "development";
7
7
  export const API_BASE_URL = process.env["NETA_API_BASE_URL"] ?? "https://api.talesofai.com";
8
- export const AUTH_API_BASE_URL = process.env["NETA_AUTH_API_BASE_URL"] ?? "https://auth.talesofai.com";
8
+ export const AUTH_API_BASE_URL = process.env["NETA_AUTH_API_BASE_URL"] ?? "https://auth.neta.art";
9
9
  export const CLIENT_ID = process.env["NETA_CLIENT_ID"] ?? "ft6zb5zp7fqmq8y807okv";
10
10
  export const NETA_TOKEN = process.env["NETA_TOKEN"] ?? "";
11
11
  export const IS_GLOBAL = API_BASE_URL.endsWith("talesofai.com");
@@ -0,0 +1,25 @@
1
+ need_login: Need to login to the NETA account, run command "login --help" for more information or set NETA_TOKEN environment variable for authentication.
2
+ not_supported_in_current_region: This command is not supported in the current region, please set NETA_TOKEN environment variable for authentication.
3
+ premium_plans_config_not_found: Premium subscription plans config not found
4
+ premium_plans_config_invalid: Premium subscription plans config is invalid
5
+ order_not_unpaid: Order is not unpaid
6
+ order_expired: Order is expired
7
+ sts_token_expired: STS token expired
8
+ artifact_not_found: Artifact not found
9
+ operation_timeout: Timeout
10
+ upload_file_type_not_supported: Media file type not supported
11
+ upload_file_size_too_large: Media file size too large, maximum size is {max_size} bytes
12
+ invalid_picture_artifact_uuid: Input is not a valid picture artifact UUID
13
+ tcp_missing_id_or_name: Either name or uuid must be provided.
14
+ tcp_not_found: Character or elementum not found {identifier}
15
+ tcp_type_mismatch_character: The resolved type is "character", but parent_type="elementum" was specified. Please adjust parent_type or use a different name to search.
16
+ tcp_type_mismatch_elementum: The resolved type is "elementum", but parent_type="character" was specified. Please adjust parent_type or use a different name to search.
17
+ tcp_unknown_type: Unknown TCP type {type}
18
+ hashtag_not_linked_to_activity_space: Hashtag "{hashtag}" is not linked to any activity space.
19
+ collection_artifact_count_range: artifacts must be between 1 and 12
20
+ collection_artifact_not_found: artifact {artifact_uuid} not found
21
+ collection_artifact_not_success: artifact {artifact_uuid} status is not success
22
+ creative_collection_not_found: collection {uuid} not found
23
+ action_fail: "{action} fail"
24
+ create_comment_fail: create_comment fail
25
+ collection_not_found: Collection "{uuid}" not found
@@ -1,5 +1,7 @@
1
+ import { Type } from "@sinclair/typebox";
1
2
  import { AxiosError } from "axios";
2
3
  import { safeParseJson } from "./json.js";
4
+ import { parseMeta } from "./parse_meta.js";
3
5
  export class ApiResponseError extends Error {
4
6
  code;
5
7
  message;
@@ -62,3 +64,30 @@ export const handleAxiosError = (error) => {
62
64
  cause: error,
63
65
  });
64
66
  };
67
+ export const errors = parseMeta(Type.Object({
68
+ need_login: Type.String(),
69
+ not_supported_in_current_region: Type.String(),
70
+ premium_plans_config_not_found: Type.String(),
71
+ premium_plans_config_invalid: Type.String(),
72
+ order_not_unpaid: Type.String(),
73
+ order_expired: Type.String(),
74
+ sts_token_expired: Type.String(),
75
+ artifact_not_found: Type.String(),
76
+ operation_timeout: Type.String(),
77
+ upload_file_type_not_supported: Type.String(),
78
+ upload_file_size_too_large: Type.String(),
79
+ invalid_picture_artifact_uuid: Type.String(),
80
+ tcp_missing_id_or_name: Type.String(),
81
+ tcp_not_found: Type.String(),
82
+ tcp_type_mismatch_character: Type.String(),
83
+ tcp_type_mismatch_elementum: Type.String(),
84
+ tcp_unknown_type: Type.String(),
85
+ hashtag_not_linked_to_activity_space: Type.String(),
86
+ collection_artifact_count_range: Type.String(),
87
+ collection_artifact_not_found: Type.String(),
88
+ collection_artifact_not_success: Type.String(),
89
+ creative_collection_not_found: Type.String(),
90
+ action_fail: Type.String(),
91
+ create_comment_fail: Type.String(),
92
+ collection_not_found: Type.String(),
93
+ }), import.meta);
@@ -0,0 +1,25 @@
1
+ need_login: 需要登录到 NETA 账户, 请运行 "login --help" 获取更多信息或设置 NETA_TOKEN 环境变量进行身份验证
2
+ not_supported_in_current_region: 当前区域不支持此命令, 请设置 NETA_TOKEN 环境变量进行身份验证
3
+ premium_plans_config_not_found: 未找到会员订阅方案配置
4
+ premium_plans_config_invalid: 会员订阅方案配置无效
5
+ order_not_unpaid: 订单不是未支付状态
6
+ order_expired: 订单已过期
7
+ sts_token_expired: STS 凭证已过期
8
+ artifact_not_found: 未找到素材
9
+ operation_timeout: 操作超时
10
+ upload_file_type_not_supported: 文件类型不支持
11
+ upload_file_size_too_large: 文件大小超过最大限制, 最大支持 {max_size} 字节
12
+ invalid_picture_artifact_uuid: 输入不是有效的图片素材 UUID
13
+ tcp_missing_id_or_name: 必须提供 name 或 uuid 参数之一
14
+ tcp_not_found: 未找到角色或元素 {identifier}
15
+ tcp_type_mismatch_character: 找到的类型为 "character"(角色),但指定了 parent_type="elementum"(风格元素)。请调整 parent_type 参数或使用不同的名称搜索。
16
+ tcp_type_mismatch_elementum: 找到的类型为 "elementum"(风格元素),但指定了 parent_type="character"(角色)。请调整 parent_type 参数或使用不同的名称搜索。
17
+ tcp_unknown_type: 未知 TCP 类型 {type}
18
+ hashtag_not_linked_to_activity_space: Hashtag "{hashtag}" 未关联任何活动空间
19
+ collection_artifact_count_range: 素材数量必须在 1 到 12 之间
20
+ collection_artifact_not_found: 未找到素材 {artifact_uuid}
21
+ collection_artifact_not_success: 素材 {artifact_uuid} 状态不是成功
22
+ creative_collection_not_found: 未找到合集 {uuid}
23
+ action_fail: "{action} 失败"
24
+ create_comment_fail: 发表评论失败
25
+ collection_not_found: 未找到合集 "{uuid}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talesofai/neta-skills",
3
- "version": "0.16.2",
3
+ "version": "0.16.6",
4
4
  "description": "Neta API pi coding agent skills for interacting with Neta API to generate images, videos, songs, and manage characters/elements.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -49,23 +49,6 @@ npx skills add talesofai/neta-skills/skills/neta-adventure
49
49
  - Creating or managing visual style elements (scenes, props, clothing, poses, atmospheres, memes) → use `neta-elementum`
50
50
  3. Use this skill only when boundaries are unclear or when you need to explain which sub-skill to pick.
51
51
 
52
- ## Authorization
53
-
54
- Use this when the user needs a **logged-in Neta identity** for CLI-backed flows and no valid session exists (or you would otherwise rely on `NETA_TOKEN`).
55
-
56
- 1. **Start the flow**: run **`neta login`** (default action is `request-code`).
57
- ```bash
58
- npx -y @talesofai/neta-skills@latest login --action request-code
59
- ```
60
- This begins the OAuth **device authorization** flow stored by the CLI.
61
-
62
- 2. **Browser step**: When the command returns device-authorization fields, show the user **`verification_uri_complete`** (the ready-to-open URL), tell them to open it in a browser and finish sign-in/consent there, then **return to the chat and explicitly say the browser step is done** so you know when to continue.
63
-
64
- 3. **Complete login**: After they confirm in chat, run **`neta login --action verify-code`** to exchange the device code for tokens. On success, show the returned **account basics**: **`uuid`** (long user id), **`nick_name`**, and **`avatar_url`**.
65
- ```bash
66
- npx -y @talesofai/neta-skills@latest login --action verify-code
67
- ```
68
-
69
52
  **Region / fallback**: Device login is only supported when the CLI is using the **global** API host (see `NETA_API_BASE_URL` / `talesofai.com`). If the command errors with “not supported in the current region”, tell the user to authenticate via the **`NETA_TOKEN`** environment variable instead.
70
53
 
71
54
  ## Capability map and sub-skill overview
@@ -20,23 +20,6 @@ Determine mode before acting. Do not ask multiple questions.
20
20
 
21
21
  Once mode is established, do not re-ask.
22
22
 
23
- ## Authorization
24
-
25
- Use this when the user needs a **logged-in Neta identity** for CLI-backed flows and no valid session exists (or you would otherwise rely on `NETA_TOKEN`).
26
-
27
- 1. **Start the flow**: run **`neta login`** (default action is `request-code`).
28
- ```bash
29
- npx -y @talesofai/neta-skills@latest login --action request-code
30
- ```
31
- This begins the OAuth **device authorization** flow stored by the CLI.
32
-
33
- 2. **Browser step**: When the command returns device-authorization fields, show the user **`verification_uri_complete`** (the ready-to-open URL), tell them to open it in a browser and finish sign-in/consent there, then **return to the chat and explicitly say the browser step is done** so you know when to continue.
34
-
35
- 3. **Complete login**: After they confirm in chat, run **`neta login --action verify-code`** to exchange the device code for tokens. On success, show the returned **account basics**: **`uuid`** (long user id), **`nick_name`**, and **`avatar_url`**.
36
- ```bash
37
- npx -y @talesofai/neta-skills@latest login --action verify-code
38
- ```
39
-
40
23
  ## Commands
41
24
 
42
25
  **Create a campaign**
@@ -9,23 +9,6 @@ Guide users from inspiration to forging, completing the creation and management
9
9
 
10
10
  > This skill requires the **neta-creative** skill to use `make_image` for visual previews.
11
11
 
12
- ## Authorization
13
-
14
- Use this when the user needs a **logged-in Neta identity** for CLI-backed flows and no valid session exists (or you would otherwise rely on `NETA_TOKEN`).
15
-
16
- 1. **Start the flow**: run **`neta login`** (default action is `request-code`).
17
- ```bash
18
- npx -y @talesofai/neta-skills@latest login --action request-code
19
- ```
20
- This begins the OAuth **device authorization** flow stored by the CLI.
21
-
22
- 2. **Browser step**: When the command returns device-authorization fields, show the user **`verification_uri_complete`** (the ready-to-open URL), tell them to open it in a browser and finish sign-in/consent there, then **return to the chat and explicitly say the browser step is done** so you know when to continue.
23
-
24
- 3. **Complete login**: After they confirm in chat, run **`neta login --action verify-code`** to exchange the device code for tokens. On success, show the returned **account basics**: **`uuid`** (long user id), **`nick_name`**, and **`avatar_url`**.
25
- ```bash
26
- npx -y @talesofai/neta-skills@latest login --action verify-code
27
- ```
28
-
29
12
  ## Command Usage
30
13
 
31
14
  ### Create Character
@@ -17,23 +17,6 @@ Used to interact with the Neta API for community feed browsing, interactions, an
17
17
  3. If the user needs **systematic research or complex filtering by categories/keywords**, switch to `neta-suggest`.
18
18
  4. If the user wants to **create new content** (images/videos/songs/MVs), switch to `neta-creative`.
19
19
 
20
- ## Authorization
21
-
22
- Use this when the user needs a **logged-in Neta identity** for CLI-backed flows and no valid session exists (or you would otherwise rely on `NETA_TOKEN`).
23
-
24
- 1. **Start the flow**: run **`neta login`** (default action is `request-code`).
25
- ```bash
26
- npx -y @talesofai/neta-skills@latest login --action request-code
27
- ```
28
- This begins the OAuth **device authorization** flow stored by the CLI.
29
-
30
- 2. **Browser step**: When the command returns device-authorization fields, show the user **`verification_uri_complete`** (the ready-to-open URL), tell them to open it in a browser and finish sign-in/consent there, then **return to the chat and explicitly say the browser step is done** so you know when to continue.
31
-
32
- 3. **Complete login**: After they confirm in chat, run **`neta login --action verify-code`** to exchange the device code for tokens. On success, show the returned **account basics**: **`uuid`** (long user id), **`nick_name`**, and **`avatar_url`**.
33
- ```bash
34
- npx -y @talesofai/neta-skills@latest login --action verify-code
35
- ```
36
-
37
20
  ## Commands
38
21
 
39
22
  ### Collection
@@ -15,23 +15,6 @@ Used to interact with the Neta API for multimedia content creation, creation‑r
15
15
  2. If, during creation, you discover that the real need is more like “browse recommendations / casually explore / research topics”, combine this skill with `neta-community` or `neta-suggest` instead of overloading this skill.
16
16
  3. **Premium** (plans, orders, Stripe): use the commands below. Run **`get_current_premium_plan`** before and after checkout so the user can confirm tier (and end time if returned). Commerce needs the **global** Neta API—see the premium reference. If creation is blocked by **quota / credits (电量)** or **usage frequency (频率)**, say why in one beat; if an upgrade fits their goal, offer the path once (list plans → create order → pay)—**do not** repeat upgrade nudges in the same conversation unless the user asks.
17
17
 
18
- ## Authorization
19
-
20
- Use this when the user needs a **logged-in Neta identity** for CLI-backed flows and no valid session exists (or you would otherwise rely on `NETA_TOKEN`).
21
-
22
- 1. **Start the flow**: run **`neta login`** (default action is `request-code`).
23
- ```bash
24
- npx -y @talesofai/neta-skills@latest login --action request-code
25
- ```
26
- This begins the OAuth **device authorization** flow stored by the CLI.
27
-
28
- 2. **Browser step**: When the command returns device-authorization fields, show the user **`verification_uri_complete`** (the ready-to-open URL), tell them to open it in a browser and finish sign-in/consent there, then **return to the chat and explicitly say the browser step is done** so you know when to continue.
29
-
30
- 3. **Complete login**: After they confirm in chat, run **`neta login --action verify-code`** to exchange the device code for tokens. On success, show the returned **account basics**: **`uuid`** (long user id), **`nick_name`**, and **`avatar_url`**.
31
- ```bash
32
- npx -y @talesofai/neta-skills@latest login --action verify-code
33
- ```
34
-
35
18
  ## Commands
36
19
 
37
20
  ### Content creation
@@ -9,23 +9,6 @@ Through the "Elementum Alchemy" workflow, forge any visual concept into a reusab
9
9
 
10
10
  > This skill requires the **neta-creative** skill to use `make_image` for visual previews.
11
11
 
12
- ## Authorization
13
-
14
- Use this when the user needs a **logged-in Neta identity** for CLI-backed flows and no valid session exists (or you would otherwise rely on `NETA_TOKEN`).
15
-
16
- 1. **Start the flow**: run **`neta login`** (default action is `request-code`).
17
- ```bash
18
- npx -y @talesofai/neta-skills@latest login --action request-code
19
- ```
20
- This begins the OAuth **device authorization** flow stored by the CLI.
21
-
22
- 2. **Browser step**: When the command returns device-authorization fields, show the user **`verification_uri_complete`** (the ready-to-open URL), tell them to open it in a browser and finish sign-in/consent there, then **return to the chat and explicitly say the browser step is done** so you know when to continue.
23
-
24
- 3. **Complete login**: After they confirm in chat, run **`neta login --action verify-code`** to exchange the device code for tokens. On success, show the returned **account basics**: **`uuid`** (long user id), **`nick_name`**, and **`avatar_url`**.
25
- ```bash
26
- npx -y @talesofai/neta-skills@latest login --action verify-code
27
- ```
28
-
29
12
  ## Command Usage
30
13
 
31
14
  ### Create Elementum
@@ -17,23 +17,6 @@ Used to interact with the Neta API to browse space‑level content.
17
17
  - If needed, fetch characters and playable content inside the space.
18
18
  3. If the user says “now generate an image/video/song for this space”, first collect the relevant space/collection info here, then switch to `neta-creative` for creation.
19
19
 
20
- ## Authorization
21
-
22
- Use this when the user needs a **logged-in Neta identity** for CLI-backed flows and no valid session exists (or you would otherwise rely on `NETA_TOKEN`).
23
-
24
- 1. **Start the flow**: run **`neta login`** (default action is `request-code`).
25
- ```bash
26
- npx -y @talesofai/neta-skills@latest login --action request-code
27
- ```
28
- This begins the OAuth **device authorization** flow stored by the CLI.
29
-
30
- 2. **Browser step**: When the command returns device-authorization fields, show the user **`verification_uri_complete`** (the ready-to-open URL), tell them to open it in a browser and finish sign-in/consent there, then **return to the chat and explicitly say the browser step is done** so you know when to continue.
31
-
32
- 3. **Complete login**: After they confirm in chat, run **`neta login --action verify-code`** to exchange the device code for tokens. On success, show the returned **account basics**: **`uuid`** (long user id), **`nick_name`**, and **`avatar_url`**.
33
- ```bash
34
- npx -y @talesofai/neta-skills@latest login --action verify-code
35
- ```
36
-
37
20
  ## Space concepts
38
21
 
39
22
  > A space is a themed collection of gameplay experiences — a scene where content is produced and consumed.
@@ -12,23 +12,6 @@ description: Neta API research and recommendation skill — provide keyword/tag/
12
12
  3. Before content creation, use this skill to research topics/tags/categories, then hand off to `neta-creative` for concrete creation.
13
13
  4. When the user wants to like/comment or otherwise interact with specific works, switch to `neta-community`.
14
14
 
15
- ## Authorization
16
-
17
- Use this when the user needs a **logged-in Neta identity** for CLI-backed flows and no valid session exists (or you would otherwise rely on `NETA_TOKEN`).
18
-
19
- 1. **Start the flow**: run **`neta login`** (default action is `request-code`).
20
- ```bash
21
- npx -y @talesofai/neta-skills@latest login --action request-code
22
- ```
23
- This begins the OAuth **device authorization** flow stored by the CLI.
24
-
25
- 2. **Browser step**: When the command returns device-authorization fields, show the user **`verification_uri_complete`** (the ready-to-open URL), tell them to open it in a browser and finish sign-in/consent there, then **return to the chat and explicitly say the browser step is done** so you know when to continue.
26
-
27
- 3. **Complete login**: After they confirm in chat, run **`neta login --action verify-code`** to exchange the device code for tokens. On success, show the returned **account basics**: **`uuid`** (long user id), **`nick_name`**, and **`avatar_url`**.
28
- ```bash
29
- npx -y @talesofai/neta-skills@latest login --action verify-code
30
- ```
31
-
32
15
  ## Core capabilities
33
16
 
34
17
  ### 1. suggest_keywords — keyword suggestions