@66rpg/lumimaker 0.1.53 → 0.1.54
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/SKILL.md +2 -2
- package/skill/lumimaker/references/publish-game.md +2 -2
- package/skill/lumimaker/references/publish-project.md +1 -1
- package/skill/lumimaker/references/publish-template.md +1 -1
- package/skill/lumimaker-play-sdk/SKILL.md +6 -6
- package/skill/lumimaker-play-sdk/references/api.md +9 -6
- package/skill/lumimaker-template-spec/references/protocol.md +3 -2
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:cs(i.status??i.audit_status),changelog:i.changelog||"-",published_at:as(i.published_at),post:c}});return ae(o,r,{emptyText:n.empty_versions.text,title:E.bold(e)})}function Eo(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:E.cyan(i.data_id||"-"),data_ver:i.data_ver||"-",summary:i.summary||"-"}));return ae(o,r,{emptyText:n.empty_data.text,title:E.bold(e)})}function U(e){if(e==null||typeof e=="object")return;let t=String(e).trim();return t||void 0}function us(e){let t=U(e);if(!(!t||t==="0"))return t}function ds(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 ps(e,t){let n=t==="template"?["template_id","id"]:t==="project"?["project_id","id"]:["game_id","id"];for(let o of n){let r=us(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=ps(t,n);i&&o.push({label:i.label,value:i.id});let a=ds(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 Po(e={}){let t=U(e.message)??s("fmt","logout_done");return E.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 ss(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 Co(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 No=new Set(["login","logout","skill"]);function To(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 Do(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 Lo(e,t={stdinTty:!!process.stdin.isTTY,stdoutTty:!!process.stdout.isTTY}){return e.skipLoginPrompt||e.command&&No.has(e.command)||e.isAgent||e.isJson||e.isInteractive===!1?!1:!!(t.stdinTty&&t.stdoutTty)}function Mo(e){return!(!e.isAgent||e.skipLoginPrompt||e.command&&No.has(e.command))}import le from"node:fs";import Ke from"node:os";import fs from"node:path";var _s="analytics-identity.json";function qt(e){return fs.join(R(e),_s)}function Oo(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=Oo(e);if(n){try{let o=R(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 Oo(n?.uid)}catch{return}}function $o(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 Fo(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 qo(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=Vo(e,t,{stdio:"inherit",env:qo()}),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 hs(e,t,n){J=!1;let o=t.includes("--json")?[...t]:[...t,"--json"];return new Promise(r=>{let i="",a="",c=!1,m=Vo(e,o,{stdio:["inherit","pipe","pipe"],env:qo(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(Fo(y)+`
|
|
23
23
|
`)}),m.on("exit",l=>{J=c,a.trim()&&process.stderr.write(Fo(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 Uo(e){return Mo(e)?(Nn(s("kit","login_required_agent")),12):bs(e)}async function bs(e){if(!Lo(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),Pe({...e,skipLoginPrompt:!0}))}function xs(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 vs(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 ys(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 ks(e,t){if(!e.isInteractive||e.isAgent||e.skipNeedInputPrompt)return null;let n=ys(t);if(!n)return null;let o=await Qn(n);return Object.keys(o).length?Pe({...e,args:Kn(e.args,o),skipNeedInputPrompt:!0}):null}async function Pe(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 hs(n,o,e.isInteractive);if(vs(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(Do(i,u)){let _=await Uo(e);if(_!=null)return _}return Bt(e)?(l.rawStdout&&process.stdout.write(l.rawStdout),0):(Ss(i,a,o,u),ws(l.envelope.notice),0)}if(l.envelope.success===!1){let u=xs(l.envelope,l.code);if(c&&u!=null)return l.rawStdout&&process.stdout.write(l.rawStdout),u;let _=await ks(e,l.envelope);if(_!=null)return _;let y=l.envelope.error??{message:s("kit","op_failed")};if(To(y)){let x=await Uo(e);if(x!=null)return x}return Bt(e)?(l.rawStdout&&process.stdout.write(l.rawStdout),l.code||2):(console.error(Co(y)),l.code||2)}return l.rawStdout&&process.stdout.write(l.rawStdout),l.code}function ws(e){if(typeof e!="string"||!e.trim())return;let t=yo(e);t&&console.log(t)}function Ss(e,t,n,o){if(e==="whoami"){console.log(vo(o));return}if(e==="publish"){console.log(ko(o));return}if(e==="publish-template"||e==="publish-template-data"){console.log(wo(o,e));return}if(e==="publish-project"){console.log(So(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(jo(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(Eo(f.templates.data_packages_title.text,o.items||[]));return}console.log(Io(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(Ao(o));return}if(e==="skill"){console.log(Ro(o));return}if(e==="logout"){console.log(Po(o));return}console.log(me.green(`\u2714 ${f.common.success_default.text}`))}import js from"node:fs";import Qt from"node:path";import{fileURLToPath as As}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 Uo(e){return Mo(e)?(Nn(s("kit","login_required_agent")),12):bs(e)}async function bs(e){if(!Lo(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),Pe({...e,skipLoginPrompt:!0}))}function xs(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 vs(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 ys(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 ks(e,t){if(!e.isInteractive||e.isAgent||e.skipNeedInputPrompt)return null;let n=ys(t);if(!n)return null;let o=await Qn(n);return Object.keys(o).length?Pe({...e,args:Kn(e.args,o),skipNeedInputPrompt:!0}):null}async function Pe(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 hs(n,o,e.isInteractive);if(vs(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(Do(i,u)){let _=await Uo(e);if(_!=null)return _}return Bt(e)?(l.rawStdout&&process.stdout.write(l.rawStdout),0):(Ss(i,a,o,u),ws(l.envelope.notice),0)}if(l.envelope.success===!1){let u=xs(l.envelope,l.code);if(c&&u!=null)return l.rawStdout&&process.stdout.write(l.rawStdout),u;let _=await ks(e,l.envelope);if(_!=null)return _;let y=l.envelope.error??{message:s("kit","op_failed")};if(To(y)){let x=await Uo(e);if(x!=null)return x}return Bt(e)?(l.rawStdout&&process.stdout.write(l.rawStdout),l.code||2):(console.error(Co(y)),l.code||2)}return l.rawStdout&&process.stdout.write(l.rawStdout),l.code}function ws(e){if(typeof e!="string"||!e.trim())return;let t=yo(e);t&&console.log(t)}function Ss(e,t,n,o){if(e==="whoami"){console.log(vo(o));return}if(e==="publish"){console.log(ko(o));return}if(e==="publish-template"||e==="publish-template-data"){console.log(wo(o,e));return}if(e==="publish-project"){console.log(So(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(jo(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(Eo(f.templates.data_packages_title.text,o.items||[]));return}console.log(Io(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(Ao(o));return}if(e==="skill"){console.log(Ro(o));return}if(e==="logout"){console.log(Po(o));return}console.log(me.green(`\u2714 ${f.common.success_default.text}`))}import js from"node:fs";import Qt from"node:path";import{fileURLToPath as As}from"node:url";var Kt={name:"@66rpg/lumimaker",version:"0.1.54",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.54","@66rpg/lumimaker-darwin-arm64":"0.1.54","@66rpg/lumimaker-darwin-x64":"0.1.54"},devDependencies:{"@types/node":"^22.15.0",typescript:"^5.8.3"}};function Jo(e){let t=e.trim();return t?t.startsWith("V")||t.startsWith("v")?`v${t.slice(1)}`:`v${t}`:""}function Es(){let e=typeof Kt.version=="string"?Kt.version.trim():"";if(e)return Jo(e);let t=Qt.dirname(As(import.meta.url)),n=[Qt.resolve(t,"../package.json"),Qt.resolve(t,"../../package.json")];for(let o of n)try{let i=JSON.parse(js.readFileSync(o,"utf8")).version?.trim();if(i)return Jo(i)}catch{}}function Bo(){return"v0.1.54"}var He={CliCommandResult:"CliCommandResult",LumiMakerCommand:"LumiMakerCommand"},Ps=new Set(["publish","publish-template","publish-template-data","publish-project","login","logout","whoami","games","templates","projects","validate-template","validate","skill","init","help","version","other"]),Rs=new Set(["update","list","versions","query","data"]),Cs=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"]),Ns=new Set(["publish_aborted","cancelled"]);function Ht(e){return!!(e&&Ns.has(e))}function Go(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 Wo(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 Ko(e){if(e)return Ps.has(e)?e:"other"}function Qo(e){if(e)return Rs.has(e)?e:void 0}function Ho(e){if(e)return Cs.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 Js from"node:os";import Bs from"node:path";import Zt from"node:fs";import Ts from"node:os";import Yo from"node:path";var ze="huanguang",zo="lumimaker_npm",Xo="lumimaker_cli",Ds=`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 Ls(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 Ms(){try{let e=JSON.parse(Zt.readFileSync(Yo.join(G(),"package.json"),"utf8"));return typeof e.version=="string"?e.version:""}catch{return""}}function Os(e){try{let t=Yo.join(R(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 $s(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 Fs(e){let t=e?.env??process.env,n=e?.homeDir??Ts.homedir(),o=e?.fileConfig??(e?.skipFile?void 0:Os(n)),r=$s(o),i=o?.sensorsAnalytics,a=!0;typeof i?.enabled=="boolean"&&(a=i.enabled),Ls(t)&&(a=!1);let c=Ds,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??Ms()}}var Xt;function Re(){return Xt||(Xt=Fs()),Xt}var Us=/^\$?[A-Za-z][A-Za-z0-9_]{0,99}$/,Vs=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 qs(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 Zo(e){let t={};for(let[n,o]of Object.entries(e)){if(!Us.test(n)||Vs.has(n.toLowerCase()))continue;let r=qs(o);r!==void 0&&(t[n]=r)}return t}var Gs=100,Ws=600,Ks=700,Qs="analytics-device-id",Xe=[],Ze=new Set,z,en=!1;function er(e){try{return`${e}-${crypto.randomUUID()}`}catch{return`${e}-${Date.now()}-${Math.random().toString(36).slice(2,12)}`}}function tr(e=Js.homedir()){if(z)return z;let t=R(e),n=Bs.join(t,Qs);try{if(ue.existsSync(n)){let r=ue.readFileSync(n,"utf8").trim();if(r)return z=r,r}let o=er("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=er("session"),z}}function Hs(){let e=Number.parseInt(process.versions.node,10);return Number.isFinite(e)?e:void 0}function Ys(){return Ee()??tr()}function zs(){let e=Re(),t=$o();return{product:zo,app_id:Xo,deploy_env:e.deployEnv,env:e.deployEnv==="release"?"production":"development",release_id:e.releaseId,surface:"cli",device_type:"desktop",device_code:tr(),tool:"npm",event_schema_version:2,os_platform:process.platform,os_arch:process.arch,node_major:Hs(),logged_in:t.logged_in,uid:t.uid,$is_login_id:!!t.uid}}function Xs(e){Ze.add(e),e.finally(()=>Ze.delete(e))}async function Zs(e,t){let n=Re();if(!n.enabled)return;let o={distinct_id:Ys(),time:Date.now(),type:"track",event:e,project:ze,properties:{$lib:"Node",$lib_version:n.releaseId||"0",$lib_method:"code",...Zo({...zs(),...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(Ws)});n.showLog&&console.error(`[lumimaker analytics] ${e} ${a.status}`)}function nr(){if(en)return;en=!0;let e=Xe.splice(0);for(let t of e)Xs(Zs(t.name,t.properties).catch(()=>{}));en=!1}function tn(e,t={}){try{if(!Re().enabled)return;Xe.length>=Gs&&Xe.shift(),Xe.push({name:e,properties:t}),nr()}catch{}}async function or(e=Ks){try{if(nr(),Ze.size===0)return;await Promise.race([Promise.allSettled([...Ze]),new Promise(t=>{setTimeout(t,e).unref?.()})])}catch{}}function rr(e,t){let n=Ko(t.command)??"other",o=t.outcome??Yt(e),r=Ht(t.reason),i=n==="login"&&e===0&&t.loggedInAfter===!1,a=Go(e,{...t,command:n}),c=Wo(t.command);return{command:n,subcommand:Qo(t.subcommand),success:a,outcome:r||i?"fail":o,agent:!!t.agent,interactive:!!t.interactive,json:!!t.json,package:"lumimaker",tool:"npm",reason:Ho(t.reason)??(a?void 0:r?t.reason:o),publish_kind:c,variant:c==="game"?t.variant:void 0}}function ir(e,t){tn(He.CliCommandResult,rr(e,t))}function ea(e,t){tn(He.LumiMakerCommand,{...rr(e,t),event_schema_version:1})}async function nn(e,t={}){try{t.skip||(ir(e,t),t.rustRan||ea(e,t)),await or()}catch{}process.exit(e)}function O(e,t,n=22){return` ${K.cyan(e.padEnd(n))}${s("help",t)}`}function ar(e,t,n=22){return` ${K.gray(e.padEnd(n))}${s("help",t)}`}function na(e){return typeof e.version=="string"&&e.version.trim()?e.version.trim():(M()?.version??"").trim()}function oa(e){let t=na(e);return!t||L(t)?!0:(b.error(s("prompt","version_invalid_log",{version:t})),b.warn(s("prompt","version_invalid_hint")),!1)}function cr(){b.error(s("kit","bin_missing",{pkg:ie()??"@66rpg/lumimaker"}))}function lr(){let e=s("help","tagline").trim()||"lumimaker",t=Bo(),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.54",
|
|
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.54",
|
|
37
|
+
"@66rpg/lumimaker-darwin-arm64": "0.1.54",
|
|
38
|
+
"@66rpg/lumimaker-darwin-x64": "0.1.54"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.15.0",
|
package/skill/lumimaker/SKILL.md
CHANGED
|
@@ -64,7 +64,7 @@ npx lumimaker login
|
|
|
64
64
|
### 5. 首次发布:标题、简介和正文一并提交
|
|
65
65
|
用户第一次把作品、模板或开源工程发到平台时,**主动说明并一起做完文案**:根据工程内容拟标题、一两句简介和一篇帖子正文,口语确认后随这次发布一起提交。告诉用户不必发完再去网页粘贴。
|
|
66
66
|
- **何时**:本地还没有对应平台记录时(作品没有正式版/试玩 id、模板没有平台 origin、开源工程没有工程 id),在跑 `publish` / `publish-template` / `publish-project` **之前**完成。续发不要重写已有标题简介,也不要带 `--content`。
|
|
67
|
-
- **怎么拟**:先读 README
|
|
67
|
+
- **怎么拟**:先读 README、玩法说明、关卡或素材、界面上的文案。标题要符合作品气质,用中文;简介一两句,中文不超过 60 个字,英文及其他不超过 120 个字符;写在一起时,一个中文按两个字符计,合计不超过 120。正文可以较长,用普通文本,不要写 Markdown 标记。不要用 `game`、`template`、`untitled` 这类占位名,也不要把英文短名当成标题。
|
|
68
68
|
- **简介怎么落地**:用口语把标题和简介念给用户确认,同意后再写入工程里的展示名和简介。用户已经给了标题或简介就沿用,不要擅自改写。确认前不要发。
|
|
69
69
|
- **正文怎么落地**:把拟好的帖子正文口语确认后,作为 `--content` 加进**同一条**首发命令。不要写入 `package.json`,不要写进数据包说明,不要为此去跑 `games` / `templates` / `projects` 更新命令,也不要发完再请人去网页粘贴。续发和 `--fork` 不要带 `--content`。以后改简介或正文请用户到网页操作。
|
|
70
70
|
|
|
@@ -135,4 +135,4 @@ npx lumimaker login
|
|
|
135
135
|
|
|
136
136
|
## 发布包体积上限(与平台一致)
|
|
137
137
|
|
|
138
|
-
作品 zip、模板数据包、开源工程 zip 压缩后各 **1GB**;模板代码 zip **100MB**;封面图 **20MB**(png/jpg/jpeg/webp/gif
|
|
138
|
+
作品 zip、模板数据包、开源工程 zip 压缩后各 **1GB**;模板代码 zip **100MB**;封面图 **20MB**(png/jpg/jpeg/webp/gif)。发布作品时,`dist/` 里每个 HTML 不能超过 **5MB**,每张图片须小于 **10MB**,其他单个文件不能超过 **100MB**。超限会发布失败,先帮用户压缩或删多余文件,不要反复重试同一超大包。
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
本地还没有正式版或对应试玩记录时,**先拟文案,再构建、再发布**。主动告诉用户:简介和帖子正文可以由助手根据工程拟好,确认后随这次发布一起交上去,不必发完再去网页粘贴。
|
|
19
19
|
|
|
20
|
-
1. 读工程里的 README
|
|
20
|
+
1. 读工程里的 README、玩法说明、界面文案,拟定给人看的中文标题和一两句简介。简介:中文不超过 60 个字,英文及其他不超过 120 个字符;写在一起时,一个中文按两个字符计,合计不超过 120。不要用 `game`、`untitled` 或英文短名当标题。
|
|
21
21
|
2. 口语念给用户确认。同意后写入工程的展示名和简介。用户已经给了标题或简介就沿用。
|
|
22
22
|
3. 同时拟一篇帖子正文(普通文本,不要写 Markdown 标记),口语确认后作为 `--content` 加进**同一条**首发 `publish`。不要写入 `package.json`,不要为此跑查询或更新命令,也不要把长介绍写进数据包说明。
|
|
23
23
|
4. 确认前不要发。然后再跑构建和 `npx lumimaker publish`(带上已确认的 `--content`)。
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
2. **纯静态与本地化**:
|
|
35
35
|
- 静态产物中引用的所有资源(图片、音频、字体、脚本、样式)必须全部打包在 `dist/` 本地文件中,严禁在页面中引用在线 CDN 外链。
|
|
36
36
|
- 平台播放器会在运行时自动向页面注入 `window.LumiMakerSdk`,切勿手动将 SDK 文件打包进产物中。
|
|
37
|
-
- 打成 zip 后上限 **1GB**。封面图(png/jpg/jpeg/webp/gif)上限 **20MB**。超限先压缩或删多余资源,不要反复发同一超大包。
|
|
37
|
+
- 打成 zip 后上限 **1GB**。`dist/` 里每个 HTML 不能超过 **5MB**,每张图片须小于 **10MB**,其他单个文件不能超过 **100MB**。封面图(png/jpg/jpeg/webp/gif)上限 **20MB**。超限先压缩或删多余资源,不要反复发同一超大包。
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
@@ -21,7 +21,7 @@ npx lumimaker publish-project
|
|
|
21
21
|
|
|
22
22
|
本地还没有开源工程记录时,**先拟文案再发布**。主动告诉用户:简介和帖子正文可以由助手根据工程拟好,确认后随这次发布一起交上去,不必发完再去网页粘贴。
|
|
23
23
|
|
|
24
|
-
1.
|
|
24
|
+
1. 读工程说明和源码用途,拟定给人看的中文标题和一两句简介。简介:中文不超过 60 个字,英文及其他不超过 120 个字符;写在一起时,一个中文按两个字符计,合计不超过 120。不要用 `untitled`、`project` 或英文短名当标题。
|
|
25
25
|
2. 口语确认后再写入工程的展示名和简介。用户已经给了标题或简介就沿用。
|
|
26
26
|
3. 同时拟一篇帖子正文(普通文本,不要写 Markdown 标记),口语确认后作为 `--content` 加进**同一条**首发 `publish-project`。不要写入 `package.json`,不要为此跑更新命令。
|
|
27
27
|
4. 确认前不要发。然后再执行带 `--content` 的 `npx lumimaker publish-project`。
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
## 3. 关键交互与规则
|
|
33
33
|
|
|
34
34
|
### 3.1 首次发布与平台展示名
|
|
35
|
-
- **先拟文案再发**:本地还没有平台模板记录时,主动告诉用户简介和帖子正文可以由助手拟好,确认后随这次发布一起提交,不必发完再去网页粘贴。先读 README
|
|
35
|
+
- **先拟文案再发**:本地还没有平台模板记录时,主动告诉用户简介和帖子正文可以由助手拟好,确认后随这次发布一起提交,不必发完再去网页粘贴。先读 README、玩法说明和素材,拟定给人看的中文标题和一两句简介(中文不超过 60 个字,英文及其他不超过 120 个字符;写在一起时,一个中文按两个字符计,合计不超过 120),口语确认后再写入工程。不要用 `template`、`untitled` 或英文短名当标题。用户已经给了标题或简介就沿用。同时拟一篇帖子正文(普通文本,不要写 Markdown 标记),确认后作为 `--content` 加进**同一条**首发 `publish-template`(含 `--init`)。不要把长介绍写进数据包说明或 `package.json`,也不要为此跑更新命令。续发和 `--fork` 不要带 `--content`。续发不要重写标题简介;以后改简介或正文请用户到网页。
|
|
36
36
|
- **首发自动带包**:首次发布模板时,工程根目录下必须存在 `assets/` 且包含文件。平台会自动上传数据包,无需手动指定 `--with-assets`,也不可绑定已有的数据包 ID。
|
|
37
37
|
- **展示名填写与保护**:
|
|
38
38
|
- 首发必须提供合法的平台展示名称(支持中文,不能包含 `/` 或 `@`)。
|
|
@@ -5,7 +5,7 @@ description: 幻光文游社区播放页 SDK(window.LumiMakerSdk)接入指
|
|
|
5
5
|
|
|
6
6
|
# 播放页 SDK(LumiMakerSdk)
|
|
7
7
|
|
|
8
|
-
作品发布后,在幻光文游社区的**播放页**(宿主)里通过 iframe 运行。平台会在玩法 HTML 里注入脚本,全局对象是 **`window.LumiMakerSdk
|
|
8
|
+
作品发布后,在幻光文游社区的**播放页**(宿主)里通过 iframe 运行。平台会在玩法 HTML 里注入脚本,全局对象是 **`window.LumiMakerSdk`**。作品用它做登录、读用户、查作品权限、安全跳转、全屏。社区播放页里,下一页写 `<a href>` 包内路径,例如 `staff.html` 或 `assets/index.html`,查询和 `#` 会留在新页。不要用 `location.href`、`location.assign`、`location.replace` 换页。左键 `target="_blank"`、自定义窗口名,或按着 Ctrl / ⌘,当前作品页不换,另开窗口。需要某个包内页面的地址、又要留在当前页时,用 `signPlayHtml`;返回的地址给程序自己用,不要显示给玩家,可以放进当前作品自己的 iframe。播放页静音由 SDK 内置完成,不要对作者说明。
|
|
9
9
|
|
|
10
10
|
> **核心原则**:
|
|
11
11
|
> 1. **严禁在工程中引入 SDK 文件**:发布时平台注入。**切勿**下载 `lumimaker-game.js`、**切勿**拷进 `dist/` 或源码、**切勿**手写 `<script src="...lumimaker-game.js">`。
|
|
@@ -13,7 +13,7 @@ description: 幻光文游社区播放页 SDK(window.LumiMakerSdk)接入指
|
|
|
13
13
|
> 3. **本地 `npm run dev` 无宿主**:`ready()` 约 5 秒后 `ready: false`,`error` 为「等待主站数据超时」,属正常。不要 mock 登录或假用户。
|
|
14
14
|
> 4. **当前一期没有云存档、没有内购/下单接口**。不要承诺「SDK 会帮你存进度、买章节」。
|
|
15
15
|
|
|
16
|
-
脚本加载后会判定容器:只允许在**社区播放页 iframe
|
|
16
|
+
脚本加载后会判定容器:只允许在**社区播放页 iframe**、**官方 App**,或**同一部作品里嵌着的签名页**里玩。用浏览器地址栏直接打开托管页、或被第三方站点嵌套,会跳到社区作品帖(拿不到帖则空白页)。这是客户端劝阻,卸掉脚本即可绕过;真正鉴权仍在宿主。错误文案:「请在幻光社区或官方 App 中打开」。
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
@@ -22,7 +22,7 @@ description: 幻光文游社区播放页 SDK(window.LumiMakerSdk)接入指
|
|
|
22
22
|
- **用户信息与登录**:唤起社区登录、展示昵称/头像。未登录的试玩仍可玩离线内容。
|
|
23
23
|
- **权限与章节锁**:读 `queryGame()` 的 `can_play` / `purchased`,画作品**自己的**关卡锁或试玩结束提示。正式版未获权时播放页会盖购买遮罩,**根本不会加载**作品 iframe。
|
|
24
24
|
- **安全跳转与全屏**:回社区首页、作者页、上一页;进入/退出播放器全屏壳。公开 API **没有** `toggleFullscreen()`。
|
|
25
|
-
- **多页 HTML
|
|
25
|
+
- **多页 HTML**:下一页写 `<a href>` 包内路径,例如 `staff.html` 或 `assets/index.html`。脚本换当前页用 `openPlayHtml`。不要用 `location.href`、`location.assign`、`location.replace`。要另开窗口用 `target="_blank"`、自定义窗口名,或 `openPlayWindow`。要地址、先留在当前页,用 `signPlayHtml`。目录地址打不开。
|
|
26
26
|
|
|
27
27
|
不要用本 skill 讲发版命令;发版看 `lumimaker`。
|
|
28
28
|
|
|
@@ -146,9 +146,9 @@ declare global {
|
|
|
146
146
|
1. **不要把 SDK 打进工程**:不要放 `lumimaker-game.js`,不要手写 script 标签。发版会注入玩法 HTML(`index.html`;若已有语言/方向页也会注入那些页)。
|
|
147
147
|
2. **通信边界**:不要 `window.parent.postMessage`,不要读、打印、改 token。不要自己调社区登录、签发 token、下单、支付。
|
|
148
148
|
3. **跳转回社区**:只用 `navigateHome()` / `navigateAuthorPage()` / `navigateBack()`,不要传 URL,不要 `window.top.location`。不要用 `queryGame()` 的 `play_url` 跳转。对玩家只展示帖子链接(若发版卡片有),不要念播放页地址、作品 ID、关联帖 ID。
|
|
149
|
-
4. **玩法页之间**:写包内 `.html` 路径,例如 `staff.html` 或 `assets/index.html`。反斜杠会改成斜杠,开头的 `/` 和 `./` 会去掉。不要用 `jubenba/`、`/`、`.` 这种目录地址,也不要写 `./#id`、`/#id`(会当成打开目录,也不会变成该目录的 `index.html`)。`..`、路径里的空白、把 `?` 写进路径,都不会签名。页内滚动写 `#id`。路径后面可以带查询串和 `#`;**查询**和 **`#` 各自最长 2048 个字符,超出的部分会被截掉**。查询里**不能**使用 `auth_key`、`signature`、`expires`、`OSSAccessKeyId` 这些签名参数名(大小写不限)。链接上若已 `preventDefault
|
|
150
|
-
5. **只要地址、先不跳转**:`signPlayHtml('assets/index.html')` 只取这个包内文件的地址。当前页保持不动,也不开新窗口。`./assets/index.html` 当成 `assets/index.html`。路径后面可以带页面自己的查询和
|
|
151
|
-
6. **另开窗口**:左键 `target="_blank"`(含页面 `<base target="_blank">`)、自定义窗口名,或 Ctrl / ⌘ 点击包内页,当前页不换,另开窗口。不要写 `target="_parent"` / `_top
|
|
149
|
+
4. **玩法页之间**:写包内 `.html` 路径,例如 `staff.html` 或 `assets/index.html`。反斜杠会改成斜杠,开头的 `/` 和 `./` 会去掉。不要用 `jubenba/`、`/`、`.` 这种目录地址,也不要写 `./#id`、`/#id`(会当成打开目录,也不会变成该目录的 `index.html`)。`..`、路径里的空白、把 `?` 写进路径,都不会签名。页内滚动写 `#id`。路径后面可以带查询串和 `#`;**查询**和 **`#` 各自最长 2048 个字符,超出的部分会被截掉**。查询里**不能**使用 `auth_key`、`signature`、`expires`、`OSSAccessKeyId` 这些签名参数名(大小写不限)。链接上若已 `preventDefault`,按页面自己的处理走。脚本换当前页用 `openPlayHtml`。`openPlayHtml`、`signPlayHtml`、`openPlayWindow` 按包内路径算,不跟着当前子目录再相对一次:在 `assets/book.html` 里写 `index.html` 仍是根目录那一页。普通链接和 `window.open` 仍按浏览器相对地址算。链接写成 `../staff.html` 时,先按浏览器算出包内路径再签名。不要用 `location.href`、`location.assign`、`location.replace` 打开包内页面,页面会显示 403。`<a href>` 在没有 Navigation API 时也会拦住并签名。根目录 `index.html` 和 `assets/index.html` 是两页。对作者说明换页时,用「下一页链接」这种口语,不要把签名参数名整段念出来。
|
|
150
|
+
5. **只要地址、先不跳转**:`signPlayHtml('assets/index.html')` 只取这个包内文件的地址。当前页保持不动,也不开新窗口。`./assets/index.html` 当成 `assets/index.html`。路径后面可以带页面自己的查询和 `#`,这两段会留在返回的地址上。返回的地址给程序自己用:不要显示给玩家,不要改路径,不要套到另一个文件上,也不要把地址上的签名抄到别的路径。可以把返回的地址放进当前作品页里的 `<iframe>`。子页不会被带到社区帖。子页里的链接和 `openPlayHtml` 会重新签名,换的是这个子框,不是外层播放框。再套一层也一样:签名交到已经连上播放页的那一层。握手和登录也走这一层。外链、已经带签名的地址、`..` 和路径中的空白会失败。
|
|
151
|
+
6. **另开窗口**:左键 `target="_blank"`(含页面 `<base target="_blank">`)、自定义窗口名,或 Ctrl / ⌘ 点击包内页,当前页不换,另开窗口。不要写 `target="_parent"` / `_top`。右键、中键和把链接拖到标签栏都打不开。引擎可调 `openPlayWindow`,`window.open` 的返回值是 `null`,拿不到窗口对象。窗口名用字母开头,后面可以是字母、数字、`-`、`_`,最长 64;同一名字回到同一个窗口。`features` 只认宽、高和位置。
|
|
152
152
|
7. **购买**:正式版未获权时播放页遮罩,iframe 不会开。作品内只处理「已经进来之后」的自制锁,不要复刻社区购买墙。
|
|
153
153
|
8. **本地预览**:无宿主时 `ready()` 失败正常。不要伪造登录。图音字体继续用工程本地文件。
|
|
154
154
|
9. **没有这些能力**:云存档、内购、查权买断。播放页静音是 SDK 内置,不要对作者说明,也不要让作者写。目录 URL 不会变成该目录的 `index.html`。包内子目录里的 `.html` 可以打开。
|
|
@@ -87,9 +87,9 @@
|
|
|
87
87
|
| `navigateHome()` | void | 播放页回社区首页。 |
|
|
88
88
|
| `navigateAuthorPage()` | void | 打开作者页。 |
|
|
89
89
|
| `navigateBack()` | void | 有历史则后退,否则回作品帖。 |
|
|
90
|
-
| `openPlayHtml(html)` | `Promise<void>` | 换到包内另一个玩法 HTML,传包内路径(如 `staff.html`、`assets/index.html`)。反斜杠会改成斜杠,开头的 `/` 和 `./`
|
|
90
|
+
| `openPlayHtml(html)` | `Promise<void>` | 换到包内另一个玩法 HTML,传包内路径(如 `staff.html`、`assets/index.html`)。反斜杠会改成斜杠,开头的 `/` 和 `./` 会去掉。按包内路径算,不跟着当前子目录再相对一次。不要传目录地址,不要把查询串写进路径。`..`、路径里的空白不会签名。社区播放页里左键点包内 `<a href>` 会先拦住再签名,没有 Navigation API 时也一样。不要用 `location.href`、`location.assign`、`location.replace` 打开包内页面,会请求未签名地址,页面显示 403;脚本换当前页用本方法。查出非法路径 → `FAILED`。官方 App 直开无宿主时会失败。 |
|
|
91
91
|
| `openPlayWindow(html, options?)` | `Promise<void>` | 另开包内玩法页,路径规则与 `openPlayHtml` 相同。`options.target` 省略时是新窗口。窗口名用字母开头,后面可以是字母、数字、`-`、`_`,最长 64;同一名字回到同一个窗口。不要传 `_self`、`_parent`、`_top`。`options.features` 只保留宽、高和位置(`width` / `height` / `left` / `top` / `screenX` / `screenY`)。返回值里没有窗口对象。非法路径 → `FAILED`。 |
|
|
92
|
-
| `signPlayHtml(html, done?)` | `Promise<string>` | 只要这个包内文件的地址,例如 `staff.html` 或 `assets/index.html`。当前页保持不动,也不开新窗口。按包内路径算,不跟着当前子目录再相对一次。`./assets/index.html` 当成 `assets/index.html`。路径后可以带查询和 `#`,签好后这两段留在返回的地址上。`await`、`.then` 或回调 `(error, url)`
|
|
92
|
+
| `signPlayHtml(html, done?)` | `Promise<string>` | 只要这个包内文件的地址,例如 `staff.html` 或 `assets/index.html`。当前页保持不动,也不开新窗口。按包内路径算,不跟着当前子目录再相对一次。`./assets/index.html` 当成 `assets/index.html`。路径后可以带查询和 `#`,签好后这两段留在返回的地址上。`await`、`.then` 或回调 `(error, url)` 都能拿到。地址给程序自己用:不要显示给玩家,不要改路径,不要套到另一个文件上,也不要把地址上的签名抄到别的路径。可以把返回的地址放进当前作品页的 `<iframe>`。子页不会被带到社区帖;子页里的链接和 `openPlayHtml` 会重新签名,换的是这个子框。再套一层也一样,握手和登录走外层已经连上播放页的那一层。外链、已经带签名的地址、`..`、路径中的空白直接失败。非法路径或签名失败 → `FAILED`。官方 App 直开、没有播放页时会失败。 |
|
|
93
93
|
| `requestFullscreen()` | `Promise<void>` | 全屏**播放器外壳**,不是游戏改 `top`。须在真实点击等手势里调用。 |
|
|
94
94
|
| `exitFullscreen()` | `Promise<void>` | 退出播放器全屏。 |
|
|
95
95
|
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
|
|
98
98
|
### 4.1 多页怎么写
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
在社区播放页里,下一页写 `<a href>` 即可。脚本换当前页用 `openPlayHtml`。不要用 `location.href`、`location.assign`、`location.replace`。
|
|
101
101
|
|
|
102
102
|
查询(`?` 起)和锚点(`#` 起)**各自最长 2048 个字符**,超出的部分会被截掉。查询里**不能**使用 `auth_key`、`signature`、`expires`、`OSSAccessKeyId` 这些签名参数名(大小写不限);写进去会被当成平台已签名地址,业务查询可能补不回。
|
|
103
103
|
|
|
@@ -110,10 +110,12 @@
|
|
|
110
110
|
| `./#id`、`/#id`、`.`、`/`、`jubenba/` | **不要写**。会当成打开目录,请求失败;脚本会拦住,但也不会改成该目录的 `index.html` |
|
|
111
111
|
| 子目录 `assets/index.html`、`jubenba/search.html` | 能打开。这是包内路径,不是目录地址 |
|
|
112
112
|
| 链接上已 `preventDefault` | 脚本不再换页,让页面自己的点击处理先完成 |
|
|
113
|
+
| `location.href` / `location.assign` / `location.replace` | **不要写**。会打开未签名地址,页面显示 403。换当前页用 `<a href>` 或 `openPlayHtml` |
|
|
114
|
+
| 把链接拖到标签栏 | 不会签名,页面打不开 |
|
|
113
115
|
|
|
114
|
-
要某个包内页面的地址、又要留在当前页:`signPlayHtml('staff.html')`,也可以写成 `assets/index.html?q=关键词#intro
|
|
116
|
+
要某个包内页面的地址、又要留在当前页:`signPlayHtml('staff.html')`,也可以写成 `assets/index.html?q=关键词#intro`。返回的地址给程序自己用,不要显示给玩家,不要改路径,不要套到另一个文件上。可以把这个地址放进当前作品页的 `<iframe>`,子页里的链接只换这个子框。`openPlayHtml`、`signPlayHtml`、`openPlayWindow` 按包内路径算;普通链接和 `window.open` 仍按浏览器相对地址算。链接写成 `../staff.html` 时,先按浏览器算出包内路径再签名。
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
右键「在新标签页中打开」、中键和把链接拖到标签栏打不开。`window.open` 的返回值是 `null`。官方 App 直接打开作品、没有播放页时,换页、另开窗口和 `signPlayHtml` 都不会成功。
|
|
117
119
|
|
|
118
120
|
本地 `npm run dev` 没有这套拦截,相对链接按普通静态站工作。
|
|
119
121
|
|
|
@@ -127,7 +129,7 @@
|
|
|
127
129
|
| `LOGIN_CANCELLED` | 用户关掉登录 | 非致命,留在当前界面。 |
|
|
128
130
|
| `TIMEOUT` | 握手或桥接超时。**`ready()` 超时只返回 `ready: false`,不 throw 本码** | 本地预览按未连接处理。 |
|
|
129
131
|
| `FULLSCREEN_DENIED` | 浏览器拒绝全屏 | 只在用户点击里请求全屏。 |
|
|
130
|
-
| `UNTRUSTED_SHELL` | 不在社区播放页 iframe / 官方 App | 提示在幻光社区或官方 App 打开。 |
|
|
132
|
+
| `UNTRUSTED_SHELL` | 不在社区播放页 iframe / 官方 App / 同一作品的子页面 | 提示在幻光社区或官方 App 打开。 |
|
|
131
133
|
| `FAILED` | 其它业务或通信失败 | 通用重试提示;不要把内部报文念给玩家。 |
|
|
132
134
|
|
|
133
135
|
---
|
|
@@ -138,6 +140,7 @@
|
|
|
138
140
|
|---|---|
|
|
139
141
|
| 社区播放页 iframe | 正常握手。 |
|
|
140
142
|
| 官方 App(本窗有官方桥) | 不跳走。没有播放页 Host 时 `ready()` 仍超时,离线可玩。 |
|
|
143
|
+
| 同一作品里的子页面 | 用 `signPlayHtml` 得到的地址嵌进当前作品的 iframe 时不跳走。握手、登录和换页签名走外层已经连上的播放页。子页里的链接只换这个子框。 |
|
|
141
144
|
| 浏览器地址栏直开托管页,或第三方站点嵌套 | 跳到社区作品帖;拿不到帖则空白页。 |
|
|
142
145
|
|
|
143
146
|
不要把「直开托管地址」当成正规玩法入口。本地预览请继续 `npm run dev`,或嵌在社区播放页 / 官方 App。
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
| `slug` | **必须** | string | 模板英文短名,在平台中唯一标识本模板;格式须匹配 `^[a-z0-9][a-z0-9-]{0,62}$`。详见下方细则。 | `"quiz-react"` |
|
|
60
60
|
| `version` | **必须** | string | 模板版本号;格式须匹配 `^(?=.{1,16}$)[a-z0-9]+(?:[.-][a-z0-9]+)*$`。详见下方细则。 | `"0.1.0"`、`"1.0.0-a"` |
|
|
61
61
|
| `data_ver` | **必须** | string 或 number | 配套数据包版本;非空字符串或有限数字。详见下方细则。 | `"1"` 或 `1` |
|
|
62
|
-
| `description` | **可选** | string |
|
|
62
|
+
| `description` | **可选** | string | 模板简介。若省略或填写空字符串,按空简介处理。有内容时:中文不超过 60 个字,英文及其他不超过 120 个字符;写在一起时,一个中文按两个字符计,合计不超过 120。 | `"基于 React 的题库答题作品模板"` |
|
|
63
63
|
|
|
64
64
|
#### 字段格式细则
|
|
65
65
|
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
2. **入口文件**:`dist/` 根目录下**必须**产出 `index.html` 入口文件。可以仅产出纯 `index.html`,无需任何其他静态资源文件。
|
|
104
104
|
3. **静态资源(建议)**:若构建产物包含 JavaScript、CSS、图片等静态资源,建议放置于与 `index.html` 同级的 `dist/assets/` 目录下。
|
|
105
105
|
4. **其他产物**:除 `dist/index.html` 外,产物目录内的其他文件组织形式本协议不作强制限制。
|
|
106
|
-
5. **社区播放页能打开的玩法 HTML**:写包内 `.html` 路径,例如 `staff.html` 或 `assets/index.html`。目录地址(如 `jubenba/`)打不开,也不会自动改成该目录的 `index.html`。页内锚点写 `#id`,不要写 `./#id`。链接写法见 `lumimaker-play-sdk`。
|
|
106
|
+
5. **社区播放页能打开的玩法 HTML**:写包内 `.html` 路径,例如 `staff.html` 或 `assets/index.html`。目录地址(如 `jubenba/`)打不开,也不会自动改成该目录的 `index.html`。页内锚点写 `#id`,不要写 `./#id`。下一页用 `<a href>` 或 `openPlayHtml`,不要用 `location.href`、`location.assign`、`location.replace`。链接写法见 `lumimaker-play-sdk`。
|
|
107
107
|
|
|
108
108
|
---
|
|
109
109
|
|
|
@@ -114,3 +114,4 @@
|
|
|
114
114
|
| 1.0 | 2026-09-22 | 初始版本。明确工程目录结构、`package.json` 配置字段与脚本规范,以及 `dist/` 构建产物约定(assets 为可选,支持纯 index.html)。 |
|
|
115
115
|
| 1.1 | 2026-10-05 | 补充:社区播放页只签 `dist/` 根目录的玩法 HTML;目录地址不会回落为该目录的 `index.html`。 |
|
|
116
116
|
| 1.2 | 2026-10-09 | 播放页改为签包内 `.html` 路径,子目录里的页面可以打开;目录地址仍然不会变成该目录的 `index.html`。 |
|
|
117
|
+
| 1.3 | 2026-10-12 | 播放页换页用 `<a href>` 或 `openPlayHtml`,不要用 `location.href`、`location.assign`、`location.replace`。 |
|