rewritable 0.8.0 → 0.9.0
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/bin/rwa.mjs +66 -0
- package/package.json +1 -1
- package/seeds/rewritable.html +619 -18
- package/src/identity.mjs +3 -2
- package/src/install.mjs +206 -0
- package/src/skill-manifest.mjs +232 -4
package/bin/rwa.mjs
CHANGED
|
@@ -71,6 +71,12 @@ Usage:
|
|
|
71
71
|
editing — the returned url carries the capability
|
|
72
72
|
token in its #k= fragment. Target: --url >
|
|
73
73
|
\$RWA_HOST_URL. --json emits {id,token,url}.
|
|
74
|
+
rwa install <skill> <host> install a signed .rwa-skill.json into a skill-host
|
|
75
|
+
container, offline. Verifies the signature + runs the
|
|
76
|
+
same trust gates as the in-app dialog, then splices the
|
|
77
|
+
skill into the frozen #rwa-skills zone. Requires --yes
|
|
78
|
+
(no dialog to consent in); gate failures are final.
|
|
79
|
+
--json emits {skillId,name,kind,verified,status}.
|
|
74
80
|
rwa skin <path> <name> apply a named style preset to a rewritable in
|
|
75
81
|
place (deterministic, offline, model-free). Names:
|
|
76
82
|
notion-clean, linear-dark, editorial-serif,
|
|
@@ -826,6 +832,66 @@ function detectProductKind(fileText) {
|
|
|
826
832
|
return;
|
|
827
833
|
}
|
|
828
834
|
|
|
835
|
+
// `rwa install <skill.rwa-skill.json> <skill-host.html> [--yes|--trust] [--json]` (v0.9 §3 / I11).
|
|
836
|
+
// The offline, headless counterpart of the seed's install dialog: verify the Ed25519
|
|
837
|
+
// signature, run the SAME gates (unsigned-capability / compute-with-perms / permission
|
|
838
|
+
// grammar / dynamic-import reject), then splice the envelope into the frozen #rwa-skills
|
|
839
|
+
// zone and write atomically. No dialog to consent in → an explicit --yes/--trust is
|
|
840
|
+
// required, and gate failures (exit 3) are FINAL — --yes cannot override them. Exit codes:
|
|
841
|
+
// 1 usage, 2 file, 3 envelope/gate (reuses codeName — no verb-specific exit-4 class). See
|
|
842
|
+
// src/install.mjs. The CLI is the sole audited exception to runtime-sole-writer (Inv 39).
|
|
843
|
+
if (verb === 'install') {
|
|
844
|
+
const jsonMode = rest.includes('--json');
|
|
845
|
+
const consent = rest.includes('--yes') || rest.includes('--trust');
|
|
846
|
+
const [envPath, hostPath] = rest.filter((a) => !a.startsWith('-'));
|
|
847
|
+
const emitInstall = (payload) => {
|
|
848
|
+
if (jsonMode) { process.stderr.write(JSON.stringify(payload) + '\n'); return; }
|
|
849
|
+
const parts = [payload.code, payload.subcode].filter(Boolean);
|
|
850
|
+
let line = 'rwa install: ' + parts.join('/');
|
|
851
|
+
if (payload.details && Object.keys(payload.details).length) line += ' ' + JSON.stringify(payload.details);
|
|
852
|
+
process.stderr.write(line + '\n');
|
|
853
|
+
};
|
|
854
|
+
if (!envPath || !hostPath) {
|
|
855
|
+
emitInstall({ code: 'usage_error', subcode: 'missing_file_args', details: { usage: 'rwa install <skill.rwa-skill.json> <skill-host.html> [--yes] [--json]' } });
|
|
856
|
+
process.exitCode = 1;
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
const { installSkillFile } = await import('../src/install.mjs');
|
|
860
|
+
let result;
|
|
861
|
+
try {
|
|
862
|
+
result = await installSkillFile(envPath, hostPath, { consent });
|
|
863
|
+
} catch (e) {
|
|
864
|
+
if (e && typeof e.exitCode === 'number') {
|
|
865
|
+
emitInstall({ code: codeName(e.exitCode), subcode: e.subcode, details: e.details });
|
|
866
|
+
process.exitCode = e.exitCode;
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
throw e;
|
|
870
|
+
}
|
|
871
|
+
// Non-blocking lookalike warning (spec §3 / Inv 23) → always to stderr; --json also
|
|
872
|
+
// carries result.lookalike in the stdout object. The install already succeeded.
|
|
873
|
+
if (result.lookalike) {
|
|
874
|
+
process.stderr.write('⚠ rwa install: the name "' + result.name + '" closely matches "' + result.lookalike + '", installed from a DIFFERENT key. The author is identified by the key, not the name — review before trusting.\n');
|
|
875
|
+
}
|
|
876
|
+
// I5 — same-key rename heads-up (non-blocking, registry-derived). This author published other
|
|
877
|
+
// names in this host; surfaced so a rename reads as continuity, not a new author.
|
|
878
|
+
if (Array.isArray(result.priorNames) && result.priorNames.length) {
|
|
879
|
+
process.stderr.write('note: rwa install: this author (same key) previously published: ' + result.priorNames.join(', ') + '. The current name is "' + result.name + '" — identity is the key, not the name.\n');
|
|
880
|
+
}
|
|
881
|
+
if (jsonMode) {
|
|
882
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
883
|
+
} else {
|
|
884
|
+
const label = result.status === 'updated' ? 'Updated' : result.status === 'already_installed' ? 'Already installed' : 'Installed';
|
|
885
|
+
process.stdout.write(
|
|
886
|
+
'✓ ' + label + ' ' + result.name + ' (' + result.kind + ', ' + (result.verified ? 'verified' : 'UNVERIFIED') + ')\n' +
|
|
887
|
+
' skillId: ' + result.skillId + '\n' +
|
|
888
|
+
(result.update && result.update.added.length ? ' + added: ' + result.update.added.join(', ') + '\n' : '') +
|
|
889
|
+
(result.update && result.update.removed.length ? ' − removed: ' + result.update.removed.join(', ') + '\n' : ''),
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
|
|
829
895
|
// `rwa skin <file> <name|reset> [--l1] [--theme-only] [--json]`.
|
|
830
896
|
//
|
|
831
897
|
// DEFAULT (no --l1): deterministic, model-free theme swap. Applies a preset's
|