@66rpg/lumimaker 0.1.50 → 0.1.51
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/dist/index.js +1 -1
- package/package.json +4 -4
- package/skill/lumimaker-play-sdk/SKILL.md +160 -161
- package/skill/lumimaker-play-sdk/references/api.md +142 -141
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ ${o.join(`
|
|
|
21
21
|
`+s("common","page_info_pattern",{page:String(e.page||1),count:String(e.count||r.length),total:String(e.total)})):"";return i+a}function Be(e,t){let n=f.versions,o=[{key:"version",header:n.col_version.text,minWidth:10},{key:"status",header:n.col_status.text,minWidth:8},{key:"changelog",header:n.col_changelog.text,minWidth:12},{key:"published_at",header:n.col_published_at.text,minWidth:18},{key:"post",header:n.col_post.text,minWidth:14}],r=t.map(i=>{let c=!!i.post_id||i.post_status!=null?Number(i.post_status)===4?n.post_public.text:n.post_draft.text:"-";return{version:i.version_name||i.version||i.version_no||"-",status:ss(i.status??i.audit_status),changelog:i.changelog||"-",published_at:is(i.published_at),post:c}});return ae(o,r,{emptyText:n.empty_versions.text,title:A.bold(e)})}function Ao(e,t){let n=f.templates,o=[{key:"data_id",header:n.col_data_id.text,minWidth:14},{key:"data_ver",header:n.col_data_ver.text,minWidth:10},{key:"summary",header:n.col_data_summary.text,minWidth:12}],r=t.map(i=>({data_id:A.cyan(i.data_id||"-"),data_ver:i.data_ver||"-",summary:i.summary||"-"}));return ae(o,r,{emptyText:n.empty_data.text,title:A.bold(e)})}function U(e){if(e==null||typeof e=="object")return;let t=String(e).trim();return t||void 0}function ls(e){let t=U(e);if(!(!t||t==="0"))return t}function ms(e){if(e==null||e==="")return;let t=typeof e=="number"?e:Number(e);if(!(!Number.isFinite(t)||t===0))return t===2?s("fmt","val_demo"):t===3?f.games.val_derived.text:t===1?s("fmt","val_official"):s("fmt","val_unknown")}function us(e,t){let n=t==="template"?["template_id","id"]:t==="project"?["project_id","id"]:["game_id","id"];for(let o of n){let r=ls(e[o]);if(r)return t==="game"?{label:(typeof e.variant=="number"?e.variant:Number(e.variant))===2?s("fmt","label_game_demo_id"):s("fmt","label_game_official_id"),id:r}:t==="template"?{label:s("fmt","label_template_id"),id:r}:{label:s("fmt","label_project_id"),id:r}}}function Ge(e,t,n="game"){let o=[],r=U(t.name)??U(t.template_name)??U(t.project_name);r&&o.push({label:s("fmt","label_name"),value:r,highlight:!0});let i=us(t,n);i&&o.push({label:i.label,value:i.id});let a=ms(t.variant);a&&o.push({label:s("fmt","label_type"),value:a});let c=U(t.parent_game_id);c&&o.push({label:s("fmt","label_parent"),value:c});let m=U(t.summary);m&&o.push({label:s("fmt","label_summary"),value:m});let l=U(t.latest_version_name)??(t.latest_version_no!=null&&t.latest_version_no!==""?s("fmt","version_no",{n:t.latest_version_no}):void 0);l&&o.push({label:s("fmt","label_version"),value:l}),t.status!=null&&t.status!==""&&o.push({label:s("fmt","label_shelf"),value:bo(t.status)});let u=U(t.demo_scope);u&&o.push({label:s("fmt","label_demo_scope"),value:u});let _=U(t.play_duration);if(_&&o.push({label:s("fmt","label_play_duration"),value:_}),t.post_status!=null&&t.post_status!==""){let y=Number(t.post_status)===4;o.push({label:s("fmt","label_post_status"),value:y?f.versions.post_public.text:f.versions.post_draft.text})}return Ae(D(e,o,{status:"info"}),t)}function Eo(e={}){let t=U(e.message)??s("fmt","logout_done");return A.green(`\u2714 ${t}`)}function We(e,t){let n=f.shelf,o=s("fmt",`type_${e}`),r=e==="template"?"template_id":e==="game"?"game_id":"project_id",i=e==="template"?"template_name":e==="game"?"name":"project_name",a=[{label:s("fmt","shelf_id",{type:o}),value:t[r]||"-",highlight:!0}];return t[i]&&a.push({label:s("fmt","shelf_name",{type:o}),value:t[i]}),t.shelf_status&&a.push({label:n.label_shelf.text,value:bo(t.shelf_status)}),t.changelog&&a.push({label:n.label_changelog.text,value:t.changelog}),Ae(D(`${o}${n.card_title_shelf_updated.text}`,a,{status:"success"}),t)}function Ro(e){let t=f.skill,n=s("fmt","list_sep"),o=Array.isArray(e.copied)?e.copied.join(n):"-",r=Array.isArray(e.targets)?e.targets.join(n):"-";return D(t.card_title.text,[{label:t.label_kit.text,value:e.kit||"-"},{label:t.label_copied.text,value:o},{label:t.label_targets.text,value:r,highlight:!0}],{status:"success"})}function Ut(e){return rs(e.replace(/https?:\/\/\S*test-lumi\.66rpg\.com\S*/gi,"").replace(/https?:\/\/\S*lumi\.66rpg\.com\/api\S*/gi,"").replace(/\b(?:test-lumi\.66rpg\.com|lumi\.66rpg\.com\/api|base_lumi_api)\b/gi,""))}function Po(e){let t=f.error,n=f.error_codes,o=Ut(Je(e.message||t.default_message.text)),r=e.hint?Ut(Je(e.hint)):e.hint,i=e.server_msg?Ut(Je(e.server_msg)):"";if(!r){let a=e.code!=null?String(e.code):"";a==="2001"||o.includes("\u672A\u767B\u5F55")?r=n[2001].text:a==="2003"||o.includes("\u51ED\u8BC1\u5DF2\u8FC7\u671F")?r=n[2003].text:o.includes("\u6821\u9A8C\u5931\u8D25")||o.includes("\u54C8\u5E0C")||o.includes("MD5")||o.includes("SHA-256")||o.includes("checksum")?r=Y(n["1003_checksum"].text):o.includes("dist")||o.includes("index.html")?r=n["1001_missing_dist"].text:o.includes("scripts.")||/scripts\s/.test(o)?r=n["1001_missing_script"].text:o.includes("\u6682\u65E0\u5DF2\u901A\u8FC7\u5BA1\u6838")||o.includes("\u5C1A\u672A\u901A\u8FC7\u5BA1\u6838")||o.includes("\u8FD8\u5728\u5BA1\u6838\u4E2D")?r=n["1001_no_approved"].text:a==="2002"||a==="8004"||o.includes("\u6743\u9650")||o.includes("\u7B7E\u7EA6")?r=n[8004].text:n[a]&&(r=n[a].text)}return r&&(r=Y(r)),fo(o,{code:e.code,hint:r,serverCode:e.server_code,serverMsg:i||void 0})}var Co=new Set(["login","logout","skill"]);function No(e){let t=e.code!=null?String(e.code):"";if(t==="2001"||t==="2003")return!0;let n=e.message||"";return n.includes("\u672A\u767B\u5F55")||n.includes("\u51ED\u8BC1\u5DF2\u8FC7\u671F")||n.includes("\u767B\u5F55\u51ED\u8BC1\u5DF2\u8FC7\u671F")||n.includes("\u767B\u5F55\u5DF2\u5931\u6548")||n.includes("refresh_token \u5DF2\u5931\u6548")}function To(e,t){if(e!=="whoami"||!t||typeof t!="object")return!1;let n=t;return n.offline?!1:n.logged_in===!1||n.expired===!0}function Do(e,t={stdinTty:!!process.stdin.isTTY,stdoutTty:!!process.stdout.isTTY}){return e.skipLoginPrompt||e.command&&Co.has(e.command)||e.isAgent||e.isJson||e.isInteractive===!1?!1:!!(t.stdinTty&&t.stdoutTty)}function Lo(e){return!(!e.isAgent||e.skipLoginPrompt||e.command&&Co.has(e.command))}import le from"node:fs";import Ke from"node:os";import ds from"node:path";var ps="analytics-identity.json";function qt(e){return ds.join(P(e),ps)}function Mo(e){if(typeof e=="number"&&Number.isFinite(e)&&e>0)return String(Math.trunc(e));if(typeof e=="string"){let t=e.trim();if(/^[1-9][0-9]{0,18}$/.test(t))return t}}function Jt(e,t=Ke.homedir()){let n=Mo(e);if(n){try{let o=P(t);le.mkdirSync(o,{recursive:!0}),le.writeFileSync(qt(t),`${JSON.stringify({uid:n})}
|
|
22
22
|
`)}catch{}return n}}function Qe(e=Ke.homedir()){try{let t=qt(e);le.existsSync(t)&&le.unlinkSync(t)}catch{}}function Ee(e=Ke.homedir()){try{let t=qt(e);if(!le.existsSync(t))return;let n=JSON.parse(le.readFileSync(t,"utf8"));return Mo(n?.uid)}catch{return}}function Oo(e=Ke.homedir()){let t=Ee(e);return t?{uid:t,logged_in:!0}:{logged_in:!1}}function Bt(e){return!!(e.isJson&&!e.isAgent)}function $o(e){let t=Se(e).trim();if(!t)return"";let n=t.match(/^(?:\d{4}-\d{2}-\d{2}T[\d:.]+Z\s+)?(INFO|DEBUG|WARN|ERROR)\s+(.*)$/);if(n){let o=n[1],r=n[2];if(o==="INFO")return`${w.brandTag} ${w.badgeInfo} ${r}`;if(o==="DEBUG")return`${w.brandTag} ${w.badgeDebug} ${me.gray(r)}`;if(o==="WARN")return`${w.brandTag} ${w.badgeWarn} ${me.yellow(r)}`;if(o==="ERROR")return`${w.brandTag} ${w.badgeError} ${me.red(r)}`}return t.startsWith("[lumimaker]")?e:`${w.brandTag} ${e}`}function Vo(e){return{...process.env,...e,LUMIMAKER_FROM_KIT:"1",LUMIMAKER_TOOL:"npm"}}var J=!1;function Wt(){return J}function Gt(e,t){return J=!1,new Promise(n=>{let o=Uo(e,t,{stdio:"inherit",env:Vo()}),r=!1;o.on("spawn",()=>{r=!0,J=!0}),o.on("exit",i=>{J=r,n(i??0)}),o.on("error",i=>{J=!1,console.error(me.red(s("kit","run_failed",{e:q(i)}))),n(1)})})}function _s(e,t,n){J=!1;let o=t.includes("--json")?[...t]:[...t,"--json"];return new Promise(r=>{let i="",a="",c=!1,m=Uo(e,o,{stdio:["inherit","pipe","pipe"],env:Vo(n?{LUMIMAKER_TTY:"1"}:void 0)});m.on("spawn",()=>{c=!0,J=!0}),m.stdout.on("data",l=>{i+=l.toString("utf8")}),m.stderr.on("data",l=>{let _=(a+l.toString("utf8")).split(/\r?\n/);a=_.pop()??"";for(let y of _)y.trim()&&process.stderr.write($o(y)+`
|
|
23
23
|
`)}),m.on("exit",l=>{J=c,a.trim()&&process.stderr.write($o(a)+`
|
|
24
|
-
`);let u=i.trim();try{let _=JSON.parse(u);r({code:l??0,envelope:_,rawStdout:i})}catch{r({code:l??0,envelope:null,rawStdout:i})}}),m.on("error",l=>{J=!1,console.error(me.red(s("kit","cli_failed",{e:q(l)}))),r({code:1,envelope:null,rawStdout:""})})})}async function Fo(e){return Lo(e)?(Nn(s("kit","login_required_agent")),12):gs(e)}async function gs(e){if(!Do(e))return null;if(!await ro())return b.warn(f.common.login_cancelled.text),null;let n=await Gt(e.binPath,["login"]);return n!==0?(b.error(f.common.login_failed.text),n||1):(b.success(f.common.login_retrying.text),Re({...e,skipLoginPrompt:!0}))}function hs(e,t){if(!e)return null;let n=e,o=n.command;return o!=="need-input"&&o!=="update-available"&&o!=="login-required"?null:typeof n.code=="number"&&n.code>0?n.code:t>0?t:11}function bs(e,t,n){try{if(e==="logout"&&(n===0||t?.success===!0)){Qe();return}let o=t?.data;o&&typeof o=="object"&&o!==null&&"uid"in o&&Jt(o.uid)}catch{}}function xs(e){if(!e)return null;let t=e;if(t.command!=="need-input")return null;let n=t.data?.questions;return Array.isArray(n)&&n.length?n:null}async function vs(e,t){if(!e.isInteractive||e.isAgent||e.skipNeedInputPrompt)return null;let n=xs(t);if(!n)return null;let o=await Qn(n);return Object.keys(o).length?Re({...e,args:Kn(e.args,o),skipNeedInputPrompt:!0}):null}async function Re(e){Mn(e.binPath);let t=ce(),{binPath:n,args:o,isJson:r,command:i,subcommand:a,isAgent:c}=e,m=o.includes("--help")||o.includes("-h")||o.includes("--version")||o.includes("-V");if(i==="login"||m)return Gt(n,o);if(Bt(e)){let u=o.includes("--json")?o:[...o,"--json"],_=await Gt(n,u);if(i==="logout"&&_===0)try{Qe()}catch{}return _}let l=await _s(n,o,e.isInteractive);if(bs(i,l.envelope,l.code),await t,!l.envelope)return l.rawStdout&&process.stdout.write(l.rawStdout),l.code;if(l.envelope.success===!0){let u=l.envelope.data??{};if(To(i,u)){let _=await Fo(e);if(_!=null)return _}return Bt(e)?(l.rawStdout&&process.stdout.write(l.rawStdout),0):(ys(i,a,o,u),0)}if(l.envelope.success===!1){let u=hs(l.envelope,l.code);if(c&&u!=null)return l.rawStdout&&process.stdout.write(l.rawStdout),u;let _=await vs(e,l.envelope);if(_!=null)return _;let y=l.envelope.error??{message:s("kit","op_failed")};if(No(y)){let x=await Fo(e);if(x!=null)return x}return Bt(e)?(l.rawStdout&&process.stdout.write(l.rawStdout),l.code||2):(console.error(Po(y)),l.code||2)}return l.rawStdout&&process.stdout.write(l.rawStdout),l.code}function ys(e,t,n,o){if(e==="whoami"){console.log(vo(o));return}if(e==="publish"){console.log(yo(o));return}if(e==="publish-template"||e==="publish-template-data"){console.log(ko(o,e));return}if(e==="publish-project"){console.log(wo(o));return}if(e==="games"){if(t==="update"){console.log(We("game",o));return}if(n.includes("--query")){console.log(Ge(f.games.query_card_title.text,o,"game"));return}if(n.includes("--versions")){console.log(Be(f.games.versions_title.text,Array.isArray(o)?o:[]));return}console.log(Io(o));return}if(e==="templates"){if(t==="update"){console.log(We("template",o));return}if(n.includes("--query")){console.log(Ge(f.templates.query_card_title.text,o,"template"));return}if(n.includes("--versions")){console.log(Be(f.templates.versions_title.text,Array.isArray(o)?o:[]));return}if(n.includes("--data")){console.log(Ao(f.templates.data_packages_title.text,o.items||[]));return}console.log(So(o));return}if(e==="projects"){if(t==="update"){console.log(We("project",o));return}if(n.includes("--query")){console.log(Ge(f.projects.query_card_title.text,o,"project"));return}if(n.includes("--versions")){console.log(Be(f.projects.versions_title.text,Array.isArray(o)?o:[]));return}console.log(jo(o));return}if(e==="skill"){console.log(Ro(o));return}if(e==="logout"){console.log(Eo(o));return}console.log(me.green(`\u2714 ${f.common.success_default.text}`))}import ws from"node:fs";import Qt from"node:path";import{fileURLToPath as Ss}from"node:url";var Kt={name:"@66rpg/lumimaker",version:"0.1.
|
|
24
|
+
`);let u=i.trim();try{let _=JSON.parse(u);r({code:l??0,envelope:_,rawStdout:i})}catch{r({code:l??0,envelope:null,rawStdout:i})}}),m.on("error",l=>{J=!1,console.error(me.red(s("kit","cli_failed",{e:q(l)}))),r({code:1,envelope:null,rawStdout:""})})})}async function Fo(e){return Lo(e)?(Nn(s("kit","login_required_agent")),12):gs(e)}async function gs(e){if(!Do(e))return null;if(!await ro())return b.warn(f.common.login_cancelled.text),null;let n=await Gt(e.binPath,["login"]);return n!==0?(b.error(f.common.login_failed.text),n||1):(b.success(f.common.login_retrying.text),Re({...e,skipLoginPrompt:!0}))}function hs(e,t){if(!e)return null;let n=e,o=n.command;return o!=="need-input"&&o!=="update-available"&&o!=="login-required"?null:typeof n.code=="number"&&n.code>0?n.code:t>0?t:11}function bs(e,t,n){try{if(e==="logout"&&(n===0||t?.success===!0)){Qe();return}let o=t?.data;o&&typeof o=="object"&&o!==null&&"uid"in o&&Jt(o.uid)}catch{}}function xs(e){if(!e)return null;let t=e;if(t.command!=="need-input")return null;let n=t.data?.questions;return Array.isArray(n)&&n.length?n:null}async function vs(e,t){if(!e.isInteractive||e.isAgent||e.skipNeedInputPrompt)return null;let n=xs(t);if(!n)return null;let o=await Qn(n);return Object.keys(o).length?Re({...e,args:Kn(e.args,o),skipNeedInputPrompt:!0}):null}async function Re(e){Mn(e.binPath);let t=ce(),{binPath:n,args:o,isJson:r,command:i,subcommand:a,isAgent:c}=e,m=o.includes("--help")||o.includes("-h")||o.includes("--version")||o.includes("-V");if(i==="login"||m)return Gt(n,o);if(Bt(e)){let u=o.includes("--json")?o:[...o,"--json"],_=await Gt(n,u);if(i==="logout"&&_===0)try{Qe()}catch{}return _}let l=await _s(n,o,e.isInteractive);if(bs(i,l.envelope,l.code),await t,!l.envelope)return l.rawStdout&&process.stdout.write(l.rawStdout),l.code;if(l.envelope.success===!0){let u=l.envelope.data??{};if(To(i,u)){let _=await Fo(e);if(_!=null)return _}return Bt(e)?(l.rawStdout&&process.stdout.write(l.rawStdout),0):(ys(i,a,o,u),0)}if(l.envelope.success===!1){let u=hs(l.envelope,l.code);if(c&&u!=null)return l.rawStdout&&process.stdout.write(l.rawStdout),u;let _=await vs(e,l.envelope);if(_!=null)return _;let y=l.envelope.error??{message:s("kit","op_failed")};if(No(y)){let x=await Fo(e);if(x!=null)return x}return Bt(e)?(l.rawStdout&&process.stdout.write(l.rawStdout),l.code||2):(console.error(Po(y)),l.code||2)}return l.rawStdout&&process.stdout.write(l.rawStdout),l.code}function ys(e,t,n,o){if(e==="whoami"){console.log(vo(o));return}if(e==="publish"){console.log(yo(o));return}if(e==="publish-template"||e==="publish-template-data"){console.log(ko(o,e));return}if(e==="publish-project"){console.log(wo(o));return}if(e==="games"){if(t==="update"){console.log(We("game",o));return}if(n.includes("--query")){console.log(Ge(f.games.query_card_title.text,o,"game"));return}if(n.includes("--versions")){console.log(Be(f.games.versions_title.text,Array.isArray(o)?o:[]));return}console.log(Io(o));return}if(e==="templates"){if(t==="update"){console.log(We("template",o));return}if(n.includes("--query")){console.log(Ge(f.templates.query_card_title.text,o,"template"));return}if(n.includes("--versions")){console.log(Be(f.templates.versions_title.text,Array.isArray(o)?o:[]));return}if(n.includes("--data")){console.log(Ao(f.templates.data_packages_title.text,o.items||[]));return}console.log(So(o));return}if(e==="projects"){if(t==="update"){console.log(We("project",o));return}if(n.includes("--query")){console.log(Ge(f.projects.query_card_title.text,o,"project"));return}if(n.includes("--versions")){console.log(Be(f.projects.versions_title.text,Array.isArray(o)?o:[]));return}console.log(jo(o));return}if(e==="skill"){console.log(Ro(o));return}if(e==="logout"){console.log(Eo(o));return}console.log(me.green(`\u2714 ${f.common.success_default.text}`))}import ws from"node:fs";import Qt from"node:path";import{fileURLToPath as Ss}from"node:url";var Kt={name:"@66rpg/lumimaker",version:"0.1.51",description:"\u5E7B\u5149\u6587\u6E38\u5236\u4F5C\u5668\uFF1A\u767B\u5F55\u3001\u6821\u9A8C\u5E76\u53D1\u5E03\u5230\u5E7B\u5149\u6587\u6E38\u793E\u533A\uFF1Bvalidate-template\uFF08TS\uFF09\uFF1Blogin / publish \u7B49\u8F6C\u53D1 lumimaker",type:"module",bin:{lumimaker:"./bin/lumimaker.js"},files:["dist","bin","skill","skill-targets.json","share/*.mjs","share/internal-registry.md"],scripts:{build:"tsc --noEmit -p tsconfig.json && node ../../scripts/bundle.mjs",test:'node --import tsx --test "src/**/*.test.ts"',prepack:"node ../../scripts/minify-share.mjs inject",postpack:"node ../../scripts/minify-share.mjs restore",prepublishOnly:"npm run build && node ../../scripts/check-versions.mjs"},publishConfig:{access:"public"},engines:{node:">=20"},dependencies:{"@clack/prompts":"^1.8.0",ajv:"^8.17.1",picocolors:"^1.1.1"},optionalDependencies:{"@66rpg/lumimaker-win32-x64":"0.1.51","@66rpg/lumimaker-darwin-arm64":"0.1.51","@66rpg/lumimaker-darwin-x64":"0.1.51"},devDependencies:{"@types/node":"^22.15.0",typescript:"^5.8.3"}};function qo(e){let t=e.trim();return t?t.startsWith("V")||t.startsWith("v")?`v${t.slice(1)}`:`v${t}`:""}function Is(){let e=typeof Kt.version=="string"?Kt.version.trim():"";if(e)return qo(e);let t=Qt.dirname(Ss(import.meta.url)),n=[Qt.resolve(t,"../package.json"),Qt.resolve(t,"../../package.json")];for(let o of n)try{let i=JSON.parse(ws.readFileSync(o,"utf8")).version?.trim();if(i)return qo(i)}catch{}}function Jo(){return"v0.1.51"}var He={CliCommandResult:"CliCommandResult",LumiMakerCommand:"LumiMakerCommand"},js=new Set(["publish","publish-template","publish-template-data","publish-project","login","logout","whoami","games","templates","projects","validate-template","validate","skill","init","help","version","other"]),As=new Set(["update","list","versions","query","data"]),Es=new Set(["bin_missing","usage","version_invalid","unexpected_positional","validate_fail","need_input","update","login_required","fail","publish_aborted","publish_confirm_required","internal_command","cancelled","help"]),Rs=new Set(["publish_aborted","cancelled"]);function Ht(e){return!!(e&&Rs.has(e))}function Bo(e,t){let n=Ht(t.reason),o=t.command==="login"&&e===0&&t.loggedInAfter===!1;return e===0&&(t.outcome==null||t.outcome==="ok")&&!n&&!o}function Go(e){if(e==="publish")return"game";if(e==="publish-template")return"template";if(e==="publish-template-data")return"template_data";if(e==="publish-project")return"project"}function Wo(e){if(e)return js.has(e)?e:"other"}function Ko(e){if(e)return As.has(e)?e:void 0}function Qo(e){if(e)return Es.has(e)?e:void 0}function Yt(e){return e===0?"ok":e===10?"update":e===11?"need_input":e===12?"login_required":e===2?"usage":"fail"}import ue from"node:fs";import Us from"node:os";import Vs from"node:path";import Zt from"node:fs";import Ps from"node:os";import Ho from"node:path";var ze="huanguang",Yo="lumimaker_npm",zo="lumimaker_cli",Cs=`https://fx-it.66rpg.com:8206/sa?project=${ze}`;function zt(e,t=ze){try{let n=new URL(e);return n.protocol==="https:"&&n.hostname==="fx-it.66rpg.com"&&n.port==="8206"&&n.pathname==="/sa"&&n.searchParams.get("project")===t&&!n.username&&!n.password}catch{return!1}}function Ye(e,t){return String(e[t]??"").trim().toLowerCase()}function Ns(e){let t=Ye(e,"LUMIMAKER_NO_ANALYTICS");if(t==="1"||t==="true")return!0;let n=Ye(e,"LUMIMAKER_ANALYTICS");if(n==="0"||n==="false")return!0;let o=Ye(e,"CI");return!!(o==="true"||o==="1"||e.NODE_TEST_CONTEXT)}function Ts(){try{let e=JSON.parse(Zt.readFileSync(Ho.join(G(),"package.json"),"utf8"));return typeof e.version=="string"?e.version:""}catch{return""}}function Ds(e){try{let t=Ho.join(P(e),"config.json");if(!Zt.existsSync(t))return;let n=JSON.parse(Zt.readFileSync(t,"utf8"));return n&&typeof n=="object"?n:void 0}catch{return}}function Ls(e){let t=e?.web_config,n=typeof t?.type=="string"?t.type:"",o=typeof t?.base_cg=="string"?t.base_cg:"",r=`${n} ${o}`.toLowerCase();return r.includes("debug-lumi")?"debug":n&&n!=="1"||r.includes("test")?"test":"release"}function Ms(e){let t=e?.env??process.env,n=e?.homeDir??Ps.homedir(),o=e?.fileConfig??(e?.skipFile?void 0:Ds(n)),r=Ls(o),i=o?.sensorsAnalytics,a=!0;typeof i?.enabled=="boolean"&&(a=i.enabled),Ns(t)&&(a=!1);let c=Cs,m=typeof i?.serverUrl=="string"?i.serverUrl.trim():"";m&&zt(m)&&(c=m);let l=typeof t.LUMIMAKER_SENSORS_URL=="string"?t.LUMIMAKER_SENSORS_URL.trim():"";l&&zt(l)&&(c=l),zt(c)||(a=!1);let u=Ye(t,"LUMIMAKER_ANALYTICS_LOG"),_=r!=="release"&&(i?.showLog===!0||u==="1"||u==="true");return{enabled:a,serverUrl:c,showLog:_,deployEnv:r,releaseId:e?.releaseId??Ts()}}var Xt;function Pe(){return Xt||(Xt=Ms()),Xt}var Os=/^\$?[A-Za-z][A-Za-z0-9_]{0,99}$/,$s=new Set(["access_token","token","cookie","authorization","phone","mobile","wechat","qq","content","body","draft_title","search","query","error","message","stack","order_no","password","refresh_token","nick_name","nickname","username","lumi_username","dest","dest_dir","cwd","argv","from_dir","path","filepath"]);function Fs(e){if(e!=null){if(typeof e=="string")return e.slice(0,256);if(typeof e=="number")return Number.isFinite(e)?e:void 0;if(typeof e=="boolean"||e instanceof Date)return e;if(Array.isArray(e))return e.slice(0,20).map(t=>t.slice(0,256))}}function Xo(e){let t={};for(let[n,o]of Object.entries(e)){if(!Os.test(n)||$s.has(n.toLowerCase()))continue;let r=Fs(o);r!==void 0&&(t[n]=r)}return t}var qs=100,Js=600,Bs=700,Gs="analytics-device-id",Xe=[],Ze=new Set,z,en=!1;function Zo(e){try{return`${e}-${crypto.randomUUID()}`}catch{return`${e}-${Date.now()}-${Math.random().toString(36).slice(2,12)}`}}function er(e=Us.homedir()){if(z)return z;let t=P(e),n=Vs.join(t,Gs);try{if(ue.existsSync(n)){let r=ue.readFileSync(n,"utf8").trim();if(r)return z=r,r}let o=Zo("cli");return ue.mkdirSync(t,{recursive:!0}),ue.writeFileSync(n,`${o}
|
|
25
25
|
`,{encoding:"utf8",flag:"wx"}),z=o,o}catch{try{if(ue.existsSync(n)){let o=ue.readFileSync(n,"utf8").trim();if(o)return z=o,o}}catch{}return z=Zo("session"),z}}function Ws(){let e=Number.parseInt(process.versions.node,10);return Number.isFinite(e)?e:void 0}function Ks(){return Ee()??er()}function Qs(){let e=Pe(),t=Oo();return{product:Yo,app_id:zo,deploy_env:e.deployEnv,env:e.deployEnv==="release"?"production":"development",release_id:e.releaseId,surface:"cli",device_type:"desktop",device_code:er(),tool:"npm",event_schema_version:2,os_platform:process.platform,os_arch:process.arch,node_major:Ws(),logged_in:t.logged_in,uid:t.uid,$is_login_id:!!t.uid}}function Hs(e){Ze.add(e),e.finally(()=>Ze.delete(e))}async function Ys(e,t){let n=Pe();if(!n.enabled)return;let o={distinct_id:Ks(),time:Date.now(),type:"track",event:e,project:ze,properties:{$lib:"Node",$lib_version:n.releaseId||"0",$lib_method:"code",...Xo({...Qs(),...t})}};n.showLog&&console.error(`[lumimaker analytics] ${e}`);let r=Buffer.from(JSON.stringify(o),"utf8").toString("base64"),i=`data=${encodeURIComponent(r)}&gzip=0`,a=await fetch(n.serverUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i,redirect:"error",signal:AbortSignal.timeout(Js)});n.showLog&&console.error(`[lumimaker analytics] ${e} ${a.status}`)}function tr(){if(en)return;en=!0;let e=Xe.splice(0);for(let t of e)Hs(Ys(t.name,t.properties).catch(()=>{}));en=!1}function tn(e,t={}){try{if(!Pe().enabled)return;Xe.length>=qs&&Xe.shift(),Xe.push({name:e,properties:t}),tr()}catch{}}async function nr(e=Bs){try{if(tr(),Ze.size===0)return;await Promise.race([Promise.allSettled([...Ze]),new Promise(t=>{setTimeout(t,e).unref?.()})])}catch{}}function or(e,t){let n=Wo(t.command)??"other",o=t.outcome??Yt(e),r=Ht(t.reason),i=n==="login"&&e===0&&t.loggedInAfter===!1,a=Bo(e,{...t,command:n}),c=Go(t.command);return{command:n,subcommand:Ko(t.subcommand),success:a,outcome:r||i?"fail":o,agent:!!t.agent,interactive:!!t.interactive,json:!!t.json,package:"lumimaker",tool:"npm",reason:Qo(t.reason)??(a?void 0:r?t.reason:o),publish_kind:c,variant:c==="game"?t.variant:void 0}}function rr(e,t){tn(He.CliCommandResult,or(e,t))}function zs(e,t){tn(He.LumiMakerCommand,{...or(e,t),event_schema_version:1})}async function nn(e,t={}){try{t.skip||(rr(e,t),t.rustRan||zs(e,t)),await nr()}catch{}process.exit(e)}function O(e,t,n=22){return` ${K.cyan(e.padEnd(n))}${s("help",t)}`}function sr(e,t,n=22){return` ${K.gray(e.padEnd(n))}${s("help",t)}`}function Zs(e){return typeof e.version=="string"&&e.version.trim()?e.version.trim():(M()?.version??"").trim()}function ea(e){let t=Zs(e);return!t||L(t)?!0:(b.error(s("prompt","version_invalid_log",{version:t})),b.warn(s("prompt","version_invalid_hint")),!1)}function ar(){b.error(s("kit","bin_missing",{pkg:ie()??"@66rpg/lumimaker"}))}function cr(){let e=s("help","tagline").trim()||"lumimaker",t=Jo(),n=t?`${e} ${t}`:e;return`${K.bold(K.cyan(n))}
|
|
26
26
|
|
|
27
27
|
${K.bold(s("help","usage_heading"))}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@66rpg/lumimaker",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
4
4
|
"description": "幻光文游制作器:登录、校验并发布到幻光文游社区;validate-template(TS);login / publish 等转发 lumimaker",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"picocolors": "^1.1.1"
|
|
34
34
|
},
|
|
35
35
|
"optionalDependencies": {
|
|
36
|
-
"@66rpg/lumimaker-win32-x64": "0.1.
|
|
37
|
-
"@66rpg/lumimaker-darwin-arm64": "0.1.
|
|
38
|
-
"@66rpg/lumimaker-darwin-x64": "0.1.
|
|
36
|
+
"@66rpg/lumimaker-win32-x64": "0.1.51",
|
|
37
|
+
"@66rpg/lumimaker-darwin-arm64": "0.1.51",
|
|
38
|
+
"@66rpg/lumimaker-darwin-x64": "0.1.51"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.15.0",
|
|
@@ -1,161 +1,160 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: lumimaker-play-sdk
|
|
3
|
-
description: 幻光文游社区播放页 SDK(window.LumiMakerSdk
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 播放页 SDK(LumiMakerSdk)
|
|
7
|
-
|
|
8
|
-
作品发布后,在幻光文游社区的**播放页**(宿主)里通过 iframe 运行。平台会在玩法 HTML 里注入脚本,全局对象是 **`window.LumiMakerSdk
|
|
9
|
-
|
|
10
|
-
> **核心原则**:
|
|
11
|
-
> 1. **严禁在工程中引入 SDK 文件**:发布时平台注入。**切勿**下载 `lumimaker-game.js`、**切勿**拷进 `dist/` 或源码、**切勿**手写 `<script src="...lumimaker-game.js">`。
|
|
12
|
-
> 2. **SDK 不负责画面渲染**:画布、UI 与玩法由作品自己画。
|
|
13
|
-
> 3. **本地 `npm run dev` 无宿主**:`ready()` 约 5 秒后 `ready: false`,`error` 为「等待主站数据超时」,属正常。不要 mock 登录或假用户。
|
|
14
|
-
> 4. **当前一期没有云存档、没有内购/下单接口**。不要承诺「SDK 会帮你存进度、买章节」。
|
|
15
|
-
|
|
16
|
-
脚本加载后会判定容器:只允许在**社区播放页 iframe**或**官方 App**里玩。用浏览器地址栏直接打开托管页、或被第三方站点嵌套,会跳到社区作品帖(拿不到帖则空白页)。这是客户端劝阻,卸掉脚本即可绕过;真正鉴权仍在宿主。错误文案:「请在幻光社区或官方 App 中打开」。
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 1. 适用场景
|
|
21
|
-
|
|
22
|
-
- **用户信息与登录**:唤起社区登录、展示昵称/头像。未登录的试玩仍可玩离线内容。
|
|
23
|
-
- **权限与章节锁**:读 `queryGame()` 的 `can_play` / `purchased`,画作品**自己的**关卡锁或试玩结束提示。正式版未获权时播放页会盖购买遮罩,**根本不会加载**作品 iframe。
|
|
24
|
-
- **安全跳转与全屏**:回社区首页、作者页、上一页;进入/退出播放器全屏壳。公开 API **没有** `toggleFullscreen()`。
|
|
25
|
-
- **多页 HTML
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// host.sdk.
|
|
70
|
-
// host.sdk.
|
|
71
|
-
// host.sdk.
|
|
72
|
-
// host.sdk.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
方法签名、字段与错误码见 [API 接口规范细则](references/api.md)。
|
|
1
|
+
---
|
|
2
|
+
name: lumimaker-play-sdk
|
|
3
|
+
description: 幻光文游社区播放页 SDK(window.LumiMakerSdk)接入指引。在作品内实现用户登录、展示昵称头像、查询游玩/购买权限(章节锁)、安全返回社区、进入全屏、写多页 HTML 链接,或只要某个玩法页的地址先不跳转时使用。平台自动注入 SDK 脚本,严禁手动引入或打包 SDK 文件。播放页静音由 SDK 内置完成,不要对作者说明。当前无云存档、无内购接口。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 播放页 SDK(LumiMakerSdk)
|
|
7
|
+
|
|
8
|
+
作品发布后,在幻光文游社区的**播放页**(宿主)里通过 iframe 运行。平台会在玩法 HTML 里注入脚本,全局对象是 **`window.LumiMakerSdk`**。作品用它做登录、读用户、查作品权限、安全跳转、全屏。社区播放页里,下一页写普通链接,查询和 `#` 会留在新页。左键 `target="_blank"`、自定义窗口名,或按着 Ctrl / ⌘,当前作品页不换,另开窗口。需要某个包根页面的地址、又要留在当前页时,用 `signPlayHtml`;返回的地址给程序自己用,不要显示给玩家。播放页静音由 SDK 内置完成,不要对作者说明。
|
|
9
|
+
|
|
10
|
+
> **核心原则**:
|
|
11
|
+
> 1. **严禁在工程中引入 SDK 文件**:发布时平台注入。**切勿**下载 `lumimaker-game.js`、**切勿**拷进 `dist/` 或源码、**切勿**手写 `<script src="...lumimaker-game.js">`。
|
|
12
|
+
> 2. **SDK 不负责画面渲染**:画布、UI 与玩法由作品自己画。
|
|
13
|
+
> 3. **本地 `npm run dev` 无宿主**:`ready()` 约 5 秒后 `ready: false`,`error` 为「等待主站数据超时」,属正常。不要 mock 登录或假用户。
|
|
14
|
+
> 4. **当前一期没有云存档、没有内购/下单接口**。不要承诺「SDK 会帮你存进度、买章节」。
|
|
15
|
+
|
|
16
|
+
脚本加载后会判定容器:只允许在**社区播放页 iframe**或**官方 App**里玩。用浏览器地址栏直接打开托管页、或被第三方站点嵌套,会跳到社区作品帖(拿不到帖则空白页)。这是客户端劝阻,卸掉脚本即可绕过;真正鉴权仍在宿主。错误文案:「请在幻光社区或官方 App 中打开」。
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. 适用场景
|
|
21
|
+
|
|
22
|
+
- **用户信息与登录**:唤起社区登录、展示昵称/头像。未登录的试玩仍可玩离线内容。
|
|
23
|
+
- **权限与章节锁**:读 `queryGame()` 的 `can_play` / `purchased`,画作品**自己的**关卡锁或试玩结束提示。正式版未获权时播放页会盖购买遮罩,**根本不会加载**作品 iframe。
|
|
24
|
+
- **安全跳转与全屏**:回社区首页、作者页、上一页;进入/退出播放器全屏壳。公开 API **没有** `toggleFullscreen()`。
|
|
25
|
+
- **多页 HTML**:下一页写普通链接,一般不必调 `openPlayHtml`。要另开窗口用 `target="_blank"`、自定义窗口名,或 `openPlayWindow`。要地址、先留在当前页,用 `signPlayHtml`。子目录页面、目录地址打不开。
|
|
26
|
+
|
|
27
|
+
不要用本 skill 讲发版命令;发版看 `lumimaker`。
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 2. 快速接入示例
|
|
32
|
+
|
|
33
|
+
只通过 `window.LumiMakerSdk` 与宿主通信。**不要**自己 `postMessage`,**不要**读 token。
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
async function initPlaySdk() {
|
|
37
|
+
const sdk = window.LumiMakerSdk
|
|
38
|
+
if (!sdk) return null
|
|
39
|
+
|
|
40
|
+
const { ready, context, error } = await sdk.ready()
|
|
41
|
+
if (!ready) {
|
|
42
|
+
console.debug('未连接到宿主(本地预览正常):', error)
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!sdk.isLoggedIn()) {
|
|
47
|
+
try {
|
|
48
|
+
await sdk.requireLogin()
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if (err && err.code === 'LOGIN_CANCELLED') return null
|
|
51
|
+
throw err
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const gameInfo = await sdk.queryGame()
|
|
56
|
+
// 用 gameInfo.can_play / gameInfo.purchased 画自制章节锁
|
|
57
|
+
// 不要用 gameInfo.play_url 跳转;不要把播放地址或关联帖 ID 念给玩家
|
|
58
|
+
|
|
59
|
+
const unbind = sdk.onChange(() => {
|
|
60
|
+
// 登录态变化时刷新 UI。现网宿主尚未推送语言变更。
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
return { sdk, context, gameInfo, unbind }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
initPlaySdk().then((host) => {
|
|
67
|
+
if (!host) return
|
|
68
|
+
// host.sdk.navigateHome()
|
|
69
|
+
// host.sdk.navigateAuthorPage()
|
|
70
|
+
// host.sdk.navigateBack()
|
|
71
|
+
// host.sdk.requestFullscreen()
|
|
72
|
+
// host.sdk.exitFullscreen()
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. TypeScript 类型声明(可选)
|
|
79
|
+
|
|
80
|
+
可放进 `src/env.d.ts`:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
export {}
|
|
84
|
+
|
|
85
|
+
declare global {
|
|
86
|
+
interface Window {
|
|
87
|
+
LumiMakerSdk?: {
|
|
88
|
+
version: string
|
|
89
|
+
ready(): Promise<{ ready: boolean; context: LumiMakerContext | null; error?: string }>
|
|
90
|
+
isReady(): boolean
|
|
91
|
+
getError(): string
|
|
92
|
+
getContext(): LumiMakerContext | null
|
|
93
|
+
getGameId(): string
|
|
94
|
+
getUser(): LumiMakerUser | null
|
|
95
|
+
isLoggedIn(): boolean
|
|
96
|
+
onChange(fn: (context: LumiMakerContext | null) => void): () => void
|
|
97
|
+
requireLogin(): Promise<void>
|
|
98
|
+
queryGame(): Promise<LumiMakerGameInfo>
|
|
99
|
+
queryUser(): Promise<LumiMakerUser>
|
|
100
|
+
navigateHome(): void
|
|
101
|
+
navigateAuthorPage(): void
|
|
102
|
+
navigateBack(): void
|
|
103
|
+
openPlayHtml(html: string): Promise<void>
|
|
104
|
+
openPlayWindow(html: string, options?: { target?: string; features?: string }): Promise<void>
|
|
105
|
+
signPlayHtml(html: string, done?: (error: Error | null, url?: string) => void): Promise<string>
|
|
106
|
+
requestFullscreen(): Promise<void>
|
|
107
|
+
exitFullscreen(): Promise<void>
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface LumiMakerContext {
|
|
112
|
+
gameId: string
|
|
113
|
+
lang: string
|
|
114
|
+
user: LumiMakerUser | null
|
|
115
|
+
variant: 1 | 2 | 3
|
|
116
|
+
parentOrigin: string
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface LumiMakerUser {
|
|
120
|
+
username: string
|
|
121
|
+
nickname: string
|
|
122
|
+
avatar: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface LumiMakerGameInfo {
|
|
126
|
+
game_id: string
|
|
127
|
+
owner: string
|
|
128
|
+
name: string
|
|
129
|
+
summary: string
|
|
130
|
+
cover_url: string
|
|
131
|
+
variant: number
|
|
132
|
+
parent_game_id: string
|
|
133
|
+
can_play: boolean
|
|
134
|
+
purchased: boolean
|
|
135
|
+
play_url: string
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`parentOrigin` 只给 SDK 校验消息来源,作品业务不要用。`getGameId()` 未握手时是空字符串。`getError()` 无失败时是空字符串。
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 4. 核心规范与操作红线
|
|
145
|
+
|
|
146
|
+
1. **不要把 SDK 打进工程**:不要放 `lumimaker-game.js`,不要手写 script 标签。发版会注入玩法 HTML(`index.html`;若已有语言/方向页也会注入那些页)。
|
|
147
|
+
2. **通信边界**:不要 `window.parent.postMessage`,不要读、打印、改 token。不要自己调社区登录、签发 token、下单、支付。
|
|
148
|
+
3. **跳转回社区**:只用 `navigateHome()` / `navigateAuthorPage()` / `navigateBack()`,不要传 URL,不要 `window.top.location`。不要用 `queryGame()` 的 `play_url` 跳转。对玩家只展示帖子链接(若发版卡片有),不要念播放页地址、作品 ID、关联帖 ID。
|
|
149
|
+
4. **玩法页之间**:只使用 `dist/` **根目录**的 `.html`(与 `index.html` 同级)。不要把玩法页放进子目录,不要用 `jubenba/`、`/`、`.` 这种目录地址,也不要写 `./#id`、`/#id`(会当成打开目录)。页内滚动写 `#id`。根目录页可以带查询串和 `#`;**查询**和 **`#` 各自最长 2048 个字符,超出的部分会被截掉**。查询里**不能**使用 `auth_key`、`signature`、`expires`、`OSSAccessKeyId` 这些签名参数名(大小写不限)。链接上若已 `preventDefault`,按页面自己的处理走。已发布入口不必调 `openPlayHtml`。对作者说明换页时,用「下一页链接」这种口语,不要把签名参数名整段念出来。
|
|
150
|
+
5. **只要地址、先不跳转**:`signPlayHtml('staff.html')` 只取包根这个文件的地址。当前页保持不动,也不开新窗口。`./staff.html` 当成 `staff.html`。文件名后面可以带页面自己的查询和 `#`,这两段会留在返回的地址上。返回的地址给程序自己用:不要显示给玩家,不要改路径,不要套到另一个文件上。子目录、外链和已经带签名的地址会失败。
|
|
151
|
+
6. **另开窗口**:左键 `target="_blank"`(含页面 `<base target="_blank">`)、自定义窗口名,或 Ctrl / ⌘ 点击包根页,当前页不换,另开窗口。不要写 `target="_parent"` / `_top`。右键和中键打不开。引擎可调 `openPlayWindow`,拿不到窗口对象。窗口名用字母开头,后面可以是字母、数字、`-`、`_`,最长 64;同一名字回到同一个窗口。`features` 只认宽、高和位置。
|
|
152
|
+
7. **购买**:正式版未获权时播放页遮罩,iframe 不会开。作品内只处理「已经进来之后」的自制锁,不要复刻社区购买墙。
|
|
153
|
+
8. **本地预览**:无宿主时 `ready()` 失败正常。不要伪造登录。图音字体继续用工程本地文件。
|
|
154
|
+
9. **没有这些能力**:云存档、内购、查权买断。播放页静音是 SDK 内置,不要对作者说明,也不要让作者写。目录 URL 不会变成该目录的 `index.html`。
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 5. API 完整参考
|
|
159
|
+
|
|
160
|
+
方法签名、字段与错误码见 [API 接口规范细则](references/api.md)。
|
|
@@ -1,141 +1,142 @@
|
|
|
1
|
-
# LumiMakerSdk 接口规范细则
|
|
2
|
-
|
|
3
|
-
全局对象为 **`window.LumiMakerSdk`**。发布到幻光文游社区后,在社区播放页的 iframe(或官方 App 打开的受信壳)里运行时,平台注入该对象。脚本版本见 `version`,作品不要写死这个数字。
|
|
4
|
-
|
|
5
|
-
失败时抛出的错误对象名为 **`LumiMakerError`**,带 `code` 字符串。
|
|
6
|
-
|
|
7
|
-
作品不要自己 `postMessage`,不要读 token,不要带 Cookie 调社区接口。
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 1. 生命周期与上下文
|
|
12
|
-
|
|
13
|
-
| 方法 / 属性 | 返回类型 | 说明 |
|
|
14
|
-
|---|---|---|
|
|
15
|
-
| `version` | string | 脚本版本。作品不要依赖具体数字。 |
|
|
16
|
-
| `ready()` | `Promise<{ ready: boolean; context: LumiMakerContext \| null; error?: string }>` | 与播放页握手。约 5 秒超时。**超时不 throw**,返回 `ready: false`。受信壳超时 `error` 为「等待主站数据超时」(本地预览正常)。未受信容器立刻失败,`error` 为「请在幻光社区或官方 App 中打开」,并跳到社区作品帖。 |
|
|
17
|
-
| `isReady()` | boolean | 是否已拿到 context。 |
|
|
18
|
-
| `getError()` | string | 最近一次握手失败文案;无失败时为空字符串。不是所有通道错误都会写入。 |
|
|
19
|
-
| `getContext()` | LumiMakerContext \| null | 公开上下文。无 token、无 api 地址。 |
|
|
20
|
-
| `getGameId()` | string | 便捷读取;未握手为空字符串。 |
|
|
21
|
-
| `getUser()` | LumiMakerUser \| null | 便捷读取当前用户。 |
|
|
22
|
-
| `isLoggedIn()` | boolean | 等价于是否已有 `getUser()`。 |
|
|
23
|
-
| `onChange(fn)` | `() => void` |
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
|
30
|
-
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
|
61
|
-
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
|
87
|
-
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
|
139
|
-
|
|
|
140
|
-
|
|
141
|
-
|
|
1
|
+
# LumiMakerSdk 接口规范细则
|
|
2
|
+
|
|
3
|
+
全局对象为 **`window.LumiMakerSdk`**。发布到幻光文游社区后,在社区播放页的 iframe(或官方 App 打开的受信壳)里运行时,平台注入该对象。脚本版本见 `version`,作品不要写死这个数字。
|
|
4
|
+
|
|
5
|
+
失败时抛出的错误对象名为 **`LumiMakerError`**,带 `code` 字符串。
|
|
6
|
+
|
|
7
|
+
作品不要自己 `postMessage`,不要读 token,不要带 Cookie 调社区接口。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. 生命周期与上下文
|
|
12
|
+
|
|
13
|
+
| 方法 / 属性 | 返回类型 | 说明 |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `version` | string | 脚本版本。作品不要依赖具体数字。 |
|
|
16
|
+
| `ready()` | `Promise<{ ready: boolean; context: LumiMakerContext \| null; error?: string }>` | 与播放页握手。约 5 秒超时。**超时不 throw**,返回 `ready: false`。受信壳超时 `error` 为「等待主站数据超时」(本地预览正常)。未受信容器立刻失败,`error` 为「请在幻光社区或官方 App 中打开」,并跳到社区作品帖。 |
|
|
17
|
+
| `isReady()` | boolean | 是否已拿到 context。 |
|
|
18
|
+
| `getError()` | string | 最近一次握手失败文案;无失败时为空字符串。不是所有通道错误都会写入。 |
|
|
19
|
+
| `getContext()` | LumiMakerContext \| null | 公开上下文。无 token、无 api 地址。 |
|
|
20
|
+
| `getGameId()` | string | 便捷读取;未握手为空字符串。 |
|
|
21
|
+
| `getUser()` | LumiMakerUser \| null | 便捷读取当前用户。 |
|
|
22
|
+
| `isLoggedIn()` | boolean | 等价于是否已有 `getUser()`。 |
|
|
23
|
+
| `onChange(fn)` | `() => void` | 登录变化。`fn` 可能收到 `null`。必须用返回值取消监听。 |
|
|
24
|
+
|
|
25
|
+
### 1.1 `getContext()` 字段
|
|
26
|
+
|
|
27
|
+
| 字段 | 类型 | 说明 |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `gameId` | string | 当前作品对外 id。 |
|
|
30
|
+
| `lang` | string | 握手时播放页语言。 |
|
|
31
|
+
| `user` | object \| null | `{ username, nickname, avatar }`;未登录为 `null`。`username` 来自社区用户 id。 |
|
|
32
|
+
| `variant` | number | `1` 正式 · `2` 试玩(demo) · `3` 衍生。 |
|
|
33
|
+
| `parentOrigin` | string | 仅供 SDK 校验消息来源,作品业务不要用。 |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. 登录
|
|
38
|
+
|
|
39
|
+
| 方法 | 返回类型 | 说明 |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `requireLogin()` | `Promise<void>` | 已登录立刻成功。否则请播放页弹出社区登录。用户取消 → `LOGIN_CANCELLED`。未受信壳会先抛 `UNTRUSTED_SHELL`。 |
|
|
42
|
+
|
|
43
|
+
未登录时试玩 iframe 仍可玩离线内容。无 token 时 `queryGame()` / `queryUser()` 得到 `NOT_LOGGED_IN`。
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 3. 查询(须已登录)
|
|
48
|
+
|
|
49
|
+
游戏自己发 HTTP,`credentials: 'omit'`,只打平台 `/v1/game-sdk/*`。`game_id` 由 token 绑定,不要自己拼查询参数。
|
|
50
|
+
|
|
51
|
+
| 方法 | 返回类型 | 说明 |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `queryGame()` | `Promise<LumiMakerGameInfo>` | 当前作品货架与权限。SDK 原样返回服务端对象,不做字段校验。 |
|
|
54
|
+
| `queryUser()` | `Promise<LumiMakerUser>` | `username`、`nickname`、`avatar`。 |
|
|
55
|
+
|
|
56
|
+
### `queryGame()` 字段
|
|
57
|
+
|
|
58
|
+
| 字段 | 类型 | 说明 |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `game_id` | string | 作品 id。不要把原始 id 念给玩家。 |
|
|
61
|
+
| `owner` | string | 作者用户名。 |
|
|
62
|
+
| `name` | string | 平台显示名。 |
|
|
63
|
+
| `summary` | string | 简介。 |
|
|
64
|
+
| `cover_url` | string | 封面地址。 |
|
|
65
|
+
| `variant` | number | `1` 正式 · `2` 试玩 · `3` 衍生。 |
|
|
66
|
+
| `parent_game_id` | string | 试玩 / 衍生关联的正式版 id;没有则为空字符串。 |
|
|
67
|
+
| `price_flower` / `price_cny` / `price_usd` | number | 标价。作品内不要用它们复刻社区购买墙。 |
|
|
68
|
+
| `latest_version_no` | number | 平台最新版本序号。不要把版本 ID 念给玩家。 |
|
|
69
|
+
| `play_url` | string | 托管播放地址。**严禁用此 URL 跳转**;也不要念给玩家。 |
|
|
70
|
+
| `post_id` | string | 关联主帖 id;demo 与正式版共用;无则为空字符串。不要念给玩家。 |
|
|
71
|
+
| `status` | number | 货架 `1` 启用 / `2` 禁用。 |
|
|
72
|
+
| `purchased` | boolean | 对该可售主体是否有已支付订单;作者未下单为 `false`。 |
|
|
73
|
+
| `can_play` | boolean | 能拿到 token 时为 `true`。用来画自制章节锁。 |
|
|
74
|
+
| `ai_model` / `engine` / `languages` / `orientations` / `play_duration` | 可选 | 可能缺省。 |
|
|
75
|
+
|
|
76
|
+
自制 UI 读 `can_play` / `purchased`。正式版未获权时播放页不会加载 iframe。
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 4. 安全跳转与全屏
|
|
81
|
+
|
|
82
|
+
只允许下列无参方法。不要操作 `window.top.location`,不要传入自定义 URL。公开 API **没有** `toggleFullscreen()`(协议里宿主可认全屏 `toggle`,游戏不要调)。
|
|
83
|
+
|
|
84
|
+
| 方法 | 返回类型 | 说明 |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `navigateHome()` | void | 播放页回社区首页。 |
|
|
87
|
+
| `navigateAuthorPage()` | void | 打开作者页。 |
|
|
88
|
+
| `navigateBack()` | void | 有历史则后退,否则回作品帖。 |
|
|
89
|
+
| `openPlayHtml(html)` | `Promise<void>` | 换到 **包根** 另一个玩法 HTML,只传文件名(如 `staff.html`)。不要传目录、不要把查询串写进这个参数给签名接口。社区 iframe 会自动拦 `<a>` / `assign` / `replace`(Chromium 还能拦 `location.href =`),已发布入口一般不必调。查出非法文件名 → `FAILED`。官方 App 直开无宿主时会失败。 |
|
|
90
|
+
| `openPlayWindow(html, options?)` | `Promise<void>` | 另开包根玩法页。`options.target` 省略时是新窗口。窗口名用字母开头,后面可以是字母、数字、`-`、`_`,最长 64;同一名字回到同一个窗口。不要传 `_self`、`_parent`、`_top`。`options.features` 只保留宽、高和位置(`width` / `height` / `left` / `top` / `screenX` / `screenY`)。返回值里没有窗口对象。非法文件名 → `FAILED`。 |
|
|
91
|
+
| `signPlayHtml(html, done?)` | `Promise<string>` | 只要包根这个文件的地址。当前页保持不动,也不开新窗口。`./staff.html` 当成 `staff.html`。文件名后可以带查询和 `#`,签好后这两段留在返回的地址上。`await`、`.then` 或回调 `(error, url)` 都能拿到。地址给程序自己用:不要显示给玩家,不要改路径,不要套到另一个文件上。子目录、外链、已经带签名的地址直接失败。非法文件名或签名失败 → `FAILED`。官方 App 直开、没有播放页时会失败。 |
|
|
92
|
+
| `requestFullscreen()` | `Promise<void>` | 全屏**播放器外壳**,不是游戏改 `top`。须在真实点击等手势里调用。 |
|
|
93
|
+
| `exitFullscreen()` | `Promise<void>` | 退出播放器全屏。 |
|
|
94
|
+
|
|
95
|
+
浏览器拒绝全屏 → `FULLSCREEN_DENIED`。
|
|
96
|
+
|
|
97
|
+
### 4.1 多页怎么写
|
|
98
|
+
|
|
99
|
+
在社区播放页里,下一页写普通链接即可,不必再调 `openPlayHtml`。
|
|
100
|
+
|
|
101
|
+
查询(`?` 起)和锚点(`#` 起)**各自最长 2048 个字符**,超出的部分会被截掉。查询里**不能**使用 `auth_key`、`signature`、`expires`、`OSSAccessKeyId` 这些签名参数名(大小写不限);写进去会被当成平台已签名地址,业务查询可能补不回。
|
|
102
|
+
|
|
103
|
+
| 写法 | 播放页里 |
|
|
104
|
+
|---|---|
|
|
105
|
+
| 包根 `staff.html`、`search.html?q=`、`snapshot.html#u=…` | 能打开。查询串和 `#` 在新页补回,不要指望它们出现在签名请求里。查询和 `#` **各自最长 2048 个字符**,超出截掉。查询里不要用 `auth_key`、`signature`、`expires`、`OSSAccessKeyId`(大小写不限),这些是平台签名参数名。再次打开同一页时,业务查询和 `#` 按这次链接整段替换 |
|
|
106
|
+
| 左键 `target="_blank"`、自定义窗口名,或 Ctrl / ⌘ | 当前页不换。播放页另开可刷新的社区窗口。含 `<base target="_blank">`。页面已 `preventDefault` 则不打开 |
|
|
107
|
+
| `target="_parent"` / `_top` | 不要写。不换当前页,也不跳出作品框 |
|
|
108
|
+
| 当前页 `#id` | 只滚动,不换页 |
|
|
109
|
+
| `./#id`、`/#id`、`.`、`/`、`jubenba/` | **不要写**。会当成打开目录,请求失败;脚本会拦住,但也不会改成该目录的 `index.html` |
|
|
110
|
+
| 子目录 `jubenba/search.html` | 打不开。玩法 HTML 放在 `dist/` 根目录 |
|
|
111
|
+
| 链接上已 `preventDefault` | 脚本不再换页,让页面自己的点击处理先完成 |
|
|
112
|
+
|
|
113
|
+
要某个包根页面的地址、又要留在当前页:`signPlayHtml('staff.html')`,也可以写成 `staff.html?q=关键词#intro`。返回的地址给程序自己用,不要显示给玩家,不要改路径,不要套到另一个文件上。
|
|
114
|
+
|
|
115
|
+
右键「在新标签页中打开」和中键打不开。官方 App 直接打开作品、没有播放页时,换页、另开窗口和 `signPlayHtml` 都不会成功。
|
|
116
|
+
|
|
117
|
+
本地 `npm run dev` 没有这套拦截,相对链接按普通静态站工作。
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 5. 错误码
|
|
122
|
+
|
|
123
|
+
| `code` | 何时 | 处理 |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `NOT_LOGGED_IN` | 无 token,或查询鉴权失败 | 需要登录时再 `requireLogin()`;试玩可继续离线。 |
|
|
126
|
+
| `LOGIN_CANCELLED` | 用户关掉登录 | 非致命,留在当前界面。 |
|
|
127
|
+
| `TIMEOUT` | 握手或桥接超时。**`ready()` 超时只返回 `ready: false`,不 throw 本码** | 本地预览按未连接处理。 |
|
|
128
|
+
| `FULLSCREEN_DENIED` | 浏览器拒绝全屏 | 只在用户点击里请求全屏。 |
|
|
129
|
+
| `UNTRUSTED_SHELL` | 不在社区播放页 iframe / 官方 App | 提示在幻光社区或官方 App 打开。 |
|
|
130
|
+
| `FAILED` | 其它业务或通信失败 | 通用重试提示;不要把内部报文念给玩家。 |
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 6. 容器(作者需要知道的行为)
|
|
135
|
+
|
|
136
|
+
| 环境 | 行为 |
|
|
137
|
+
|---|---|
|
|
138
|
+
| 社区播放页 iframe | 正常握手。 |
|
|
139
|
+
| 官方 App(本窗有官方桥) | 不跳走。没有播放页 Host 时 `ready()` 仍超时,离线可玩。 |
|
|
140
|
+
| 浏览器地址栏直开托管页,或第三方站点嵌套 | 跳到社区作品帖;拿不到帖则空白页。 |
|
|
141
|
+
|
|
142
|
+
不要把「直开托管地址」当成正规玩法入口。本地预览请继续 `npm run dev`,或嵌在社区播放页 / 官方 App。
|