@stage5/lumine 0.2.6 → 0.2.8
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/lib/api.js +23 -0
- package/lib/commands.js +353 -1
- package/lib/constants.js +30 -0
- package/lib/doctor.js +720 -0
- package/lib/util.js +7 -0
- package/lib/workspace.js +56 -0
- package/package.json +1 -1
- package/sdk/BUILD_SDK_INDEX.md +35 -3
package/lib/util.js
CHANGED
|
@@ -86,6 +86,13 @@ export function defaultMainCheckoutDir(build) {
|
|
|
86
86
|
return `twinkle-main-${titleSlug || "build"}-${buildId}`;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
export function defaultVersionCheckoutDir(build, versionNumber) {
|
|
90
|
+
const titleSlug = slugify(build?.title || "");
|
|
91
|
+
const buildId = Number(build?.id || 0) || "build";
|
|
92
|
+
const version = Number(versionNumber || 0) || "version";
|
|
93
|
+
return `twinkle-v${version}-${titleSlug || "build"}-${buildId}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
89
96
|
export function resolveRequiredBuildId(value) {
|
|
90
97
|
const buildId = resolveBuildReference(value).buildId;
|
|
91
98
|
if (buildId > 0) return buildId;
|
package/lib/workspace.js
CHANGED
|
@@ -491,6 +491,54 @@ export async function writeMainCheckoutMetadata({
|
|
|
491
491
|
);
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
+
// Metadata for a read-only `pull --version <n>` checkout of one previous save:
|
|
495
|
+
// versionCheckout marks it so save can point back at the restore flow.
|
|
496
|
+
export async function writeVersionCheckoutMetadata({
|
|
497
|
+
dir,
|
|
498
|
+
options,
|
|
499
|
+
build,
|
|
500
|
+
manifest,
|
|
501
|
+
version,
|
|
502
|
+
pulledAt,
|
|
503
|
+
}) {
|
|
504
|
+
const metadataDir = path.join(dir, PROJECT_METADATA_DIR);
|
|
505
|
+
await fs.mkdir(metadataDir, { recursive: true });
|
|
506
|
+
const buildId = Number(build?.id || 0) || null;
|
|
507
|
+
await fs.writeFile(
|
|
508
|
+
path.join(metadataDir, PROJECT_METADATA_FILE),
|
|
509
|
+
JSON.stringify(
|
|
510
|
+
{
|
|
511
|
+
schemaVersion: 1,
|
|
512
|
+
buildId,
|
|
513
|
+
readOnly: true,
|
|
514
|
+
versionCheckout: true,
|
|
515
|
+
checkoutVersion: Number(version?.version || 0) || null,
|
|
516
|
+
checkoutVersionSummary: version?.summary || null,
|
|
517
|
+
checkoutVersionCreatedAt: Number(version?.createdAt || 0) || null,
|
|
518
|
+
build: {
|
|
519
|
+
id: buildId,
|
|
520
|
+
title: build?.title || (buildId ? `Build ${buildId}` : ""),
|
|
521
|
+
role: build?.role || "collaborator",
|
|
522
|
+
ownerUsername: build?.ownerUsername || null,
|
|
523
|
+
contributionStatus: "none",
|
|
524
|
+
contributionRootBuildId:
|
|
525
|
+
Number(build?.contributionRootBuildId || 0) || null,
|
|
526
|
+
canWrite: false,
|
|
527
|
+
canPublish: false,
|
|
528
|
+
},
|
|
529
|
+
apiUrl: options.apiUrl,
|
|
530
|
+
siteUrl: options.siteUrl,
|
|
531
|
+
lumineCli: serializeLumineCliMetadata(options),
|
|
532
|
+
manifest,
|
|
533
|
+
pulledAt,
|
|
534
|
+
},
|
|
535
|
+
null,
|
|
536
|
+
2,
|
|
537
|
+
),
|
|
538
|
+
"utf8",
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
|
|
494
542
|
export async function findLocalProjectMetadata(startDir) {
|
|
495
543
|
let current = path.resolve(startDir || process.cwd());
|
|
496
544
|
while (true) {
|
|
@@ -527,6 +575,14 @@ export function assertLocalProjectCanBeSaved(localProject) {
|
|
|
527
575
|
`This is a read-only checkout of main${rootBuildId ? ` for Build ${rootBuildId}` : ""}. Make edits in your branch workspace (\`lumine pull${rootBuildId ? ` ${rootBuildId}` : ""}\`), and run \`lumine update-from-main\` there to bring main's changes into it.`,
|
|
528
576
|
);
|
|
529
577
|
}
|
|
578
|
+
if (metadata.versionCheckout === true) {
|
|
579
|
+
const checkoutBuildId =
|
|
580
|
+
Number(metadata.buildId || 0) || Number(metadata.build?.id || 0) || 0;
|
|
581
|
+
const checkoutVersion = Number(metadata.checkoutVersion || 0) || 0;
|
|
582
|
+
throw new Error(
|
|
583
|
+
`This is a read-only checkout of a previous save${checkoutVersion ? ` (v${checkoutVersion})` : ""}${checkoutBuildId ? ` for Build ${checkoutBuildId}` : ""}. To bring this save back, run \`lumine restore${checkoutVersion ? ` ${checkoutVersion}` : " <n>"}\` from the editable workspace, then \`lumine save\`.`,
|
|
584
|
+
);
|
|
585
|
+
}
|
|
530
586
|
if (isReadOnlyReferenceMetadata(metadata)) {
|
|
531
587
|
const sourceBuildId =
|
|
532
588
|
Number(metadata.reference?.sourceBuildId || 0) ||
|
package/package.json
CHANGED
package/sdk/BUILD_SDK_INDEX.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Build SDK Index
|
|
2
2
|
|
|
3
|
-
Version: 1.26.
|
|
4
|
-
Updated: 2026-
|
|
5
|
-
Generated: 2026-
|
|
3
|
+
Version: 1.26.3
|
|
4
|
+
Updated: 2026-07-08
|
|
5
|
+
Generated: 2026-07-08T09:25:17.959Z
|
|
6
6
|
|
|
7
7
|
## Notes
|
|
8
8
|
- This SDK is injected into Build iframes via the Build preview/runtime.
|
|
@@ -21,12 +21,28 @@ Generated: 2026-06-18T12:26:29.648Z
|
|
|
21
21
|
- Use Twinkle.world for realtime multiplayer rooms, avatar presence, movement, emotes, and lightweight actions; world sessions are disposable and durable MMO state belongs in sharedDb/privateDb.
|
|
22
22
|
- Use Twinkle.characters.chat for real Zero/Ciel NPC dialogue with shared room context and AI Energy-aware thinking modes.
|
|
23
23
|
- Twinkle.ai.chat history entries must use { role, content }; map local message.text fields to content before passing history.
|
|
24
|
+
- Interface text must not be selectable on touch devices: apply user-select: none plus -webkit-user-select: none and -webkit-touch-callout: none to interface text (HUD, buttons, labels, menus, scores, game controls) so mobile long-press does not highlight UI. Keep text inputs and genuinely user-copyable content selectable.
|
|
25
|
+
- Build app tab mute is enforced by the host runtime automatically for standard media elements and Web Audio connections to AudioContext.destination. Apps with custom audio engines can also observe Twinkle.onAudioMuteChange and check Twinkle.isAudioMuted.
|
|
24
26
|
|
|
25
27
|
## Token Scopes
|
|
26
28
|
files:read, user:read, users:read, dailyReflections:read, content:read, sharedDb:read, sharedDb:write, privateDb:read, privateDb:write, files:write, chat:read, chat:write, notifications:read, notifications:write, notifications:emit, reminders:read, reminders:write
|
|
27
29
|
|
|
28
30
|
## Namespaces
|
|
29
31
|
|
|
32
|
+
### Twinkle
|
|
33
|
+
- isAudioMuted() | scopes: none
|
|
34
|
+
- Returns: boolean
|
|
35
|
+
- Returns whether the host runtime currently has this Build app tab muted.
|
|
36
|
+
- The host automatically mutes standard <audio>/<video> elements and Web Audio nodes connected to AudioContext.destination.
|
|
37
|
+
- Use this only when your app manages audio outside those standard paths.
|
|
38
|
+
- Example: if (Twinkle.isAudioMuted()) pauseCustomMixerOutput();
|
|
39
|
+
- onAudioMuteChange(listener, options?) | scopes: none
|
|
40
|
+
- Returns: unsubscribe function
|
|
41
|
+
- Subscribe to host tab mute changes for custom audio engines.
|
|
42
|
+
- The listener receives the current muted boolean immediately by default.
|
|
43
|
+
- Pass { immediate: false } to skip the initial callback.
|
|
44
|
+
- Example: const unsubscribe = Twinkle.onAudioMuteChange((muted) => customMixer.setMuted(muted));
|
|
45
|
+
|
|
30
46
|
### Twinkle.capabilities
|
|
31
47
|
- async get() | scopes: none
|
|
32
48
|
- Returns: Capability snapshot
|
|
@@ -49,6 +65,22 @@ files:read, user:read, users:read, dailyReflections:read, content:read, sharedDb
|
|
|
49
65
|
- Returns: Viewer info
|
|
50
66
|
- Forces a fresh fetch from the parent.
|
|
51
67
|
|
|
68
|
+
### Twinkle.app
|
|
69
|
+
- async getInfo() | scopes: none
|
|
70
|
+
- Returns: App info object from the parent (includes appUrl) or null
|
|
71
|
+
- Cached after the first call for the iframe session.
|
|
72
|
+
- async getShareUrl(pathSegment) | scopes: none
|
|
73
|
+
- Returns: Canonical shareable deep-link URL string, or null when app info is unavailable
|
|
74
|
+
- Builds a canonical shareable deep link into this app, e.g. https://www.twin-kle.com/app/884/432-the-great-gatsby.
|
|
75
|
+
- Example: await Twinkle.app.getShareUrl('432-the-great-gatsby');
|
|
76
|
+
- async navigate(target) | scopes: none
|
|
77
|
+
- Returns: { success, src }
|
|
78
|
+
- Navigate to another Build preview route through the parent bridge without dropping Twinkle SDK access.
|
|
79
|
+
- Use this for in-app Build preview/world switches instead of window.location.assign, location.replace, or setting location.href.
|
|
80
|
+
- The parent validates that the target is still a Build preview URL before navigating.
|
|
81
|
+
- External URLs are rejected and do not receive the Build bridge nonce.
|
|
82
|
+
- Example: await Twinkle.app.navigate('./arena.html');
|
|
83
|
+
|
|
52
84
|
### Twinkle.preview
|
|
53
85
|
- getLayout() | scopes: none
|
|
54
86
|
- Returns: { mode, viewport, stage, safeInsets, playfield }
|