@wairon/sdk 5.0.1-dev.5
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/README.md +61 -0
- package/dist/index.d.ts +219 -0
- package/dist/index.js +705 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @wairon/sdk
|
|
2
|
+
|
|
3
|
+
The pack-archive format authority and authoring toolkit for [wairon](https://github.com/SYW-Apps/Waffle-AIron).
|
|
4
|
+
|
|
5
|
+
It owns the portable, versioned **`.wpack`** archive format: a ZIP whose root
|
|
6
|
+
carries a `wairon-pack.yaml` envelope (identity, format/runtime compatibility,
|
|
7
|
+
optional per-entry integrity) wrapping an ordinary directory pack (`pack.yaml`
|
|
8
|
+
for declarative packs, `pack.cjs` for code packs, `skills/**/SKILL.md`, …).
|
|
9
|
+
|
|
10
|
+
It is deliberately **standalone and dependency-free** with respect to the rest
|
|
11
|
+
of wairon, so third-party pack authors can depend on only `@wairon/sdk`. It does
|
|
12
|
+
STRUCTURAL / envelope validation only — semantic declarative-pack validation
|
|
13
|
+
stays in wairon core and runs after extraction.
|
|
14
|
+
|
|
15
|
+
## API
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import {
|
|
19
|
+
scaffoldPack, // scaffold a new pack project (declarative | code)
|
|
20
|
+
buildPack, // build an installable .wpack from a pack directory
|
|
21
|
+
inspectArchive, // inspect + version-check a .wpack without extracting
|
|
22
|
+
extractPack, // SAFELY extract a .wpack under enforced limits
|
|
23
|
+
} from '@wairon/sdk';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Safety model
|
|
27
|
+
|
|
28
|
+
`extractPack` never writes anything unsafe: before a single byte is inflated,
|
|
29
|
+
the codec plans the extraction from the archive's central-directory metadata and
|
|
30
|
+
rejects zip-slip (`..`, absolute, backslash, drive-letter paths), symlinks and
|
|
31
|
+
non-regular entries, and anything over the caps (entry count, total inflated
|
|
32
|
+
size, per-entry size, compression ratio, path depth). Integrity is verified
|
|
33
|
+
before the tree is written, so a tampered pack never lands.
|
|
34
|
+
|
|
35
|
+
Whole in-memory buffers only (never streams) — the zip-bomb guard must enumerate
|
|
36
|
+
entry sizes *before* decompression. The `bytes` surface is `Uint8Array` (Node's
|
|
37
|
+
`Buffer` is accepted transparently).
|
|
38
|
+
|
|
39
|
+
### Authoring code packs
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { defineRule, type RuleContext, type Finding } from '@wairon/sdk';
|
|
43
|
+
|
|
44
|
+
export default {
|
|
45
|
+
name: 'my-rules',
|
|
46
|
+
rules: [
|
|
47
|
+
defineRule({
|
|
48
|
+
name: 'my-example',
|
|
49
|
+
description: 'Example architectural doctrine.',
|
|
50
|
+
codes: ['MY_EXAMPLE'],
|
|
51
|
+
check(ctx: RuleContext): Finding[] {
|
|
52
|
+
return [];
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `.wpack` envelope, serialized as `wairon-pack.yaml` at the archive root —
|
|
3
|
+
* the portable, versioned identity + integrity header wrapping a directory pack.
|
|
4
|
+
* `kind` is "declarative" (data only) or "code" (executable entry / rules).
|
|
5
|
+
*/
|
|
6
|
+
interface PackArchiveManifest {
|
|
7
|
+
/** Pack-archive format version (currently 1); a newer major is rejected, not silently mis-read. */
|
|
8
|
+
formatVersion: number;
|
|
9
|
+
/** Pack identity; must equal the inner pack manifest's name. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Pack version (semver); must equal the inner pack manifest's version. */
|
|
12
|
+
version: string;
|
|
13
|
+
/** "declarative" (data only) | "code" (executable entry / programmatic rules). */
|
|
14
|
+
kind: string;
|
|
15
|
+
/** Archive-relative path to the pack entry file (e.g. "pack.yaml", "pack.cjs"). */
|
|
16
|
+
entry: string;
|
|
17
|
+
/** Minimum compatible wairon version (semver range). */
|
|
18
|
+
minWaironVersion?: string;
|
|
19
|
+
/** Optional integrity digest over the manifest. */
|
|
20
|
+
digest?: string;
|
|
21
|
+
/** Optional per-entry integrity map: archive path -> sha256. */
|
|
22
|
+
entryDigests?: Record<string, string>;
|
|
23
|
+
/** Producer stamp, e.g. "@wairon/sdk@X.Y.Z". */
|
|
24
|
+
generatedBy?: string;
|
|
25
|
+
/** ISO-8601 build timestamp. */
|
|
26
|
+
generatedAt?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The result of inspecting a `.wpack` without extracting it: its envelope
|
|
30
|
+
* identity plus computed archive stats and a compatibility verdict.
|
|
31
|
+
*/
|
|
32
|
+
interface PackArchiveInfo {
|
|
33
|
+
name: string;
|
|
34
|
+
version: string;
|
|
35
|
+
/** "declarative" | "code". */
|
|
36
|
+
kind: string;
|
|
37
|
+
entry: string;
|
|
38
|
+
formatVersion: number;
|
|
39
|
+
minWaironVersion?: string;
|
|
40
|
+
/** Number of regular file entries in the archive. */
|
|
41
|
+
entryCount: number;
|
|
42
|
+
totalUncompressedBytes: number;
|
|
43
|
+
/** Whether formatVersion + minWaironVersion are compatible with the running wairon. */
|
|
44
|
+
compatible: boolean;
|
|
45
|
+
/** Present when integrity data was carried: true if every entry digest matched. */
|
|
46
|
+
integrityVerified?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The outcome of safely extracting a `.wpack` into a destination directory:
|
|
50
|
+
* where it landed, its parsed envelope, and the archive-relative entry path.
|
|
51
|
+
*/
|
|
52
|
+
interface PackExtractionResult {
|
|
53
|
+
/** Absolute path the pack was extracted into (e.g. .wai/packs/<name>). */
|
|
54
|
+
directory: string;
|
|
55
|
+
/** Path to the pack entry file within `directory`. */
|
|
56
|
+
entryPath: string;
|
|
57
|
+
/** The parsed envelope. */
|
|
58
|
+
manifest: PackArchiveManifest;
|
|
59
|
+
/** Pack name (convenience mirror of manifest.name). */
|
|
60
|
+
name: string;
|
|
61
|
+
/** "declarative" | "code". */
|
|
62
|
+
kind: string;
|
|
63
|
+
/** Number of files written. */
|
|
64
|
+
entryCount: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The configurable safety caps the codec enforces when planning an extraction —
|
|
68
|
+
* the zip-bomb / oversized-archive guardrails. Every field is optional; unset
|
|
69
|
+
* fields fall back to the codec's default profile.
|
|
70
|
+
*/
|
|
71
|
+
interface PackExtractionLimits {
|
|
72
|
+
/** Max entry count (default 4096). */
|
|
73
|
+
maxEntries?: number;
|
|
74
|
+
/** Max total inflated size (default 32 MiB; hosted 8 MiB). */
|
|
75
|
+
maxTotalUncompressedBytes?: number;
|
|
76
|
+
/** Max single-entry inflated size (default 8 MiB). */
|
|
77
|
+
maxEntryBytes?: number;
|
|
78
|
+
/** Max inflated:compressed ratio, zip-bomb guard (default 100). */
|
|
79
|
+
maxCompressionRatio?: number;
|
|
80
|
+
/** Max path nesting depth (default 16). */
|
|
81
|
+
maxDepth?: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Inputs to `wairon pack init`: the identity and variant of the pack project to
|
|
85
|
+
* scaffold, and where to write it.
|
|
86
|
+
*/
|
|
87
|
+
interface PackScaffoldRequest {
|
|
88
|
+
/** Pack name (also the default directory + archive stem). */
|
|
89
|
+
name: string;
|
|
90
|
+
/** Initial version (default 0.1.0). */
|
|
91
|
+
version?: string;
|
|
92
|
+
/** "declarative" | "code". */
|
|
93
|
+
kind: string;
|
|
94
|
+
/** Directory to scaffold the pack project into. */
|
|
95
|
+
targetDir: string;
|
|
96
|
+
/** Include a skills/<id>/SKILL.md stub. */
|
|
97
|
+
withSkill?: boolean;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The output of `wairon pack build`: the assembled `.wpack` archive bytes, a
|
|
101
|
+
* descriptor of what was built, and a suggested file name.
|
|
102
|
+
*/
|
|
103
|
+
interface PackBuildResult {
|
|
104
|
+
/** The assembled .wpack (ZIP) bytes. */
|
|
105
|
+
archive: Uint8Array;
|
|
106
|
+
/** Descriptor of the built archive. */
|
|
107
|
+
info: PackArchiveInfo;
|
|
108
|
+
/** e.g. "my-pack-1.0.0.wpack". */
|
|
109
|
+
suggestedFileName: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* One file within a pack: an archive-relative POSIX path and its raw bytes. The
|
|
113
|
+
* common currency between the scaffold specialist, the archive adapter, and the
|
|
114
|
+
* codec (integrity verification over file bytes).
|
|
115
|
+
*/
|
|
116
|
+
interface PackFile {
|
|
117
|
+
/** Archive-relative POSIX path (forward slashes). */
|
|
118
|
+
path: string;
|
|
119
|
+
/** Raw file bytes. */
|
|
120
|
+
contents: Uint8Array;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Metadata for one archive entry, enumerated by the archive adapter WITHOUT
|
|
124
|
+
* inflating it — so the codec can enforce zip-bomb and size caps before any
|
|
125
|
+
* bytes are decompressed.
|
|
126
|
+
*/
|
|
127
|
+
interface ArchiveEntryMeta {
|
|
128
|
+
/** Raw archive entry path (as stored, pre-normalization). */
|
|
129
|
+
path: string;
|
|
130
|
+
uncompressedSize: number;
|
|
131
|
+
compressedSize: number;
|
|
132
|
+
/** "file" | "dir" | "symlink". */
|
|
133
|
+
kind: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The codec's approved, safe extraction plan: the normalized archive-relative
|
|
137
|
+
* paths that passed every safety check, plus the total inflated size. The
|
|
138
|
+
* archive adapter inflates and writes only these paths.
|
|
139
|
+
*/
|
|
140
|
+
interface PackExtractionPlan {
|
|
141
|
+
/** Approved, normalized relative paths to inflate + write under the destination. */
|
|
142
|
+
paths: string[];
|
|
143
|
+
/** Sum of approved entries' inflated sizes. */
|
|
144
|
+
totalUncompressedBytes: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Severity of a finding a pack rule emits. */
|
|
148
|
+
type RuleSeverity = 'error' | 'warning';
|
|
149
|
+
/** A single issue a pack rule reports about the spec tree. */
|
|
150
|
+
interface Finding {
|
|
151
|
+
/** Pack-local issue code (surfaced namespaced by wairon, e.g. `<PACK>_<CODE>`). */
|
|
152
|
+
code: string;
|
|
153
|
+
/** 'error' | 'warning'. */
|
|
154
|
+
severity: RuleSeverity;
|
|
155
|
+
/** Human-readable explanation of the violation. */
|
|
156
|
+
message: string;
|
|
157
|
+
/** The spec id the finding is anchored to (optional). */
|
|
158
|
+
specId?: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A read-only spec node as seen by a pack rule: its id/name plus arbitrary
|
|
162
|
+
* further fields (kept open so the facade stays stable as the spec model grows).
|
|
163
|
+
*/
|
|
164
|
+
interface RuleSpecNode {
|
|
165
|
+
id: string;
|
|
166
|
+
name?: string;
|
|
167
|
+
[key: string]: unknown;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The minimal, stable view of the spec tree a code-pack rule inspects. A rule
|
|
171
|
+
* reads these collections and returns findings — it never mutates state and
|
|
172
|
+
* never reaches into wairon core internals.
|
|
173
|
+
*/
|
|
174
|
+
interface RuleContext {
|
|
175
|
+
/** The L0 system spec. */
|
|
176
|
+
system: RuleSpecNode;
|
|
177
|
+
/** All L1 subsystem specs. */
|
|
178
|
+
subsystems: RuleSpecNode[];
|
|
179
|
+
/** All L2 component specs. */
|
|
180
|
+
components: RuleSpecNode[];
|
|
181
|
+
/** All L3 interface specs. */
|
|
182
|
+
interfaces: RuleSpecNode[];
|
|
183
|
+
/** All L4 implementation specs. */
|
|
184
|
+
implementations: RuleSpecNode[];
|
|
185
|
+
/** All shared type/value-object specs. */
|
|
186
|
+
types: RuleSpecNode[];
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* A programmatic conformance rule shipped by a code pack. Its runtime shape —
|
|
190
|
+
* `{ name, description?, codes: string[], check }` — is what wairon's extension
|
|
191
|
+
* loader duck-types when it loads a `pack.cjs`.
|
|
192
|
+
*/
|
|
193
|
+
interface SddRule {
|
|
194
|
+
/** Stable rule id (kebab-case), e.g. "my-portal-transport". */
|
|
195
|
+
name: string;
|
|
196
|
+
/** One-line description of what the rule enforces and why. */
|
|
197
|
+
description?: string;
|
|
198
|
+
/** The issue codes this rule can emit. */
|
|
199
|
+
codes: string[];
|
|
200
|
+
/** Inspect the spec tree and return findings (empty when clean). */
|
|
201
|
+
check(ctx: RuleContext): Finding[];
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Author an SddRule with full type inference. Returns the rule unchanged — it
|
|
205
|
+
* exists purely so code packs get typed authoring without importing anything
|
|
206
|
+
* from wairon core.
|
|
207
|
+
*/
|
|
208
|
+
declare function defineRule(rule: SddRule): SddRule;
|
|
209
|
+
|
|
210
|
+
/** Scaffold a new pack project (declarative or code) into a directory; returns the created file paths. */
|
|
211
|
+
declare function scaffoldPack(request: PackScaffoldRequest): string[];
|
|
212
|
+
/** Build an installable .wpack archive from a pack directory. */
|
|
213
|
+
declare function buildPack(sourceDir: string): PackBuildResult;
|
|
214
|
+
/** Inspect + verify a .wpack archive without extracting it. */
|
|
215
|
+
declare function inspectArchive(archive: Uint8Array): PackArchiveInfo;
|
|
216
|
+
/** Safely extract a .wpack archive into a destination directory under enforced limits. */
|
|
217
|
+
declare function extractPack(archive: Uint8Array, destDir: string, limits?: PackExtractionLimits): PackExtractionResult;
|
|
218
|
+
|
|
219
|
+
export { type ArchiveEntryMeta, type Finding, type PackArchiveInfo, type PackArchiveManifest, type PackBuildResult, type PackExtractionLimits, type PackExtractionPlan, type PackExtractionResult, type PackFile, type PackScaffoldRequest, type RuleContext, type RuleSeverity, type RuleSpecNode, type SddRule, buildPack, defineRule, extractPack, inspectArchive, scaffoldPack };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// package.json
|
|
34
|
+
var require_package = __commonJS({
|
|
35
|
+
"package.json"(exports2, module2) {
|
|
36
|
+
module2.exports = {
|
|
37
|
+
name: "@wairon/sdk",
|
|
38
|
+
version: "5.0.1-dev.5",
|
|
39
|
+
description: "Wairon pack-archive (.wpack) format authority and authoring toolkit \u2014 scaffold, build, inspect, and safely extract wairon extension packs.",
|
|
40
|
+
keywords: [
|
|
41
|
+
"wairon",
|
|
42
|
+
"sdd",
|
|
43
|
+
"pack",
|
|
44
|
+
"wpack",
|
|
45
|
+
"archive",
|
|
46
|
+
"developer-tools"
|
|
47
|
+
],
|
|
48
|
+
author: "SYW",
|
|
49
|
+
license: "MIT",
|
|
50
|
+
homepage: "https://github.com/SYW-Apps/Waffle-AIron",
|
|
51
|
+
repository: {
|
|
52
|
+
type: "git",
|
|
53
|
+
url: "https://github.com/SYW-Apps/Waffle-AIron.git"
|
|
54
|
+
},
|
|
55
|
+
type: "commonjs",
|
|
56
|
+
main: "./dist/index.js",
|
|
57
|
+
types: "./dist/index.d.ts",
|
|
58
|
+
files: [
|
|
59
|
+
"dist",
|
|
60
|
+
"README.md"
|
|
61
|
+
],
|
|
62
|
+
engines: {
|
|
63
|
+
node: ">=18.0.0"
|
|
64
|
+
},
|
|
65
|
+
scripts: {
|
|
66
|
+
build: "tsup",
|
|
67
|
+
typecheck: "tsc --noEmit",
|
|
68
|
+
test: "vitest run",
|
|
69
|
+
"test:watch": "vitest",
|
|
70
|
+
clean: "rimraf dist"
|
|
71
|
+
},
|
|
72
|
+
dependencies: {
|
|
73
|
+
fflate: "^0.8.2",
|
|
74
|
+
"js-yaml": "^4.1.0"
|
|
75
|
+
},
|
|
76
|
+
devDependencies: {
|
|
77
|
+
"@types/js-yaml": "^4.0.9",
|
|
78
|
+
"@types/node": "^20.14.0",
|
|
79
|
+
rimraf: "^5.0.7",
|
|
80
|
+
tsup: "^8.1.0",
|
|
81
|
+
typescript: "^5.5.2",
|
|
82
|
+
vitest: "^4.1.9"
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// src/index.ts
|
|
89
|
+
var src_exports = {};
|
|
90
|
+
__export(src_exports, {
|
|
91
|
+
buildPack: () => buildPack2,
|
|
92
|
+
defineRule: () => defineRule,
|
|
93
|
+
extractPack: () => extractPack2,
|
|
94
|
+
inspectArchive: () => inspectArchive2,
|
|
95
|
+
scaffoldPack: () => scaffoldPack2
|
|
96
|
+
});
|
|
97
|
+
module.exports = __toCommonJS(src_exports);
|
|
98
|
+
|
|
99
|
+
// src/orchestrator.ts
|
|
100
|
+
var path2 = __toESM(require("path"));
|
|
101
|
+
var import_js_yaml2 = require("js-yaml");
|
|
102
|
+
|
|
103
|
+
// src/codec.ts
|
|
104
|
+
var import_js_yaml = require("js-yaml");
|
|
105
|
+
var import_crypto = require("crypto");
|
|
106
|
+
var ENVELOPE_FILENAME = "wairon-pack.yaml";
|
|
107
|
+
var SUPPORTED_FORMAT_VERSION = 1;
|
|
108
|
+
var DEFAULTS = {
|
|
109
|
+
maxEntries: 4096,
|
|
110
|
+
maxTotalUncompressedBytes: 32 * 1024 * 1024,
|
|
111
|
+
maxEntryBytes: 8 * 1024 * 1024,
|
|
112
|
+
maxCompressionRatio: 100,
|
|
113
|
+
maxDepth: 16
|
|
114
|
+
};
|
|
115
|
+
function parseManifest(text) {
|
|
116
|
+
const raw = (0, import_js_yaml.load)(text);
|
|
117
|
+
const manifest = validateEnvelope(raw);
|
|
118
|
+
if (Math.trunc(manifest.formatVersion) > SUPPORTED_FORMAT_VERSION) {
|
|
119
|
+
throw new Error("unsupported pack-archive formatVersion (newer than this wairon understands)");
|
|
120
|
+
}
|
|
121
|
+
return manifest;
|
|
122
|
+
}
|
|
123
|
+
function serializeManifest(manifest) {
|
|
124
|
+
const clean = {
|
|
125
|
+
formatVersion: manifest.formatVersion,
|
|
126
|
+
name: manifest.name,
|
|
127
|
+
version: manifest.version,
|
|
128
|
+
kind: manifest.kind,
|
|
129
|
+
entry: manifest.entry
|
|
130
|
+
};
|
|
131
|
+
if (manifest.minWaironVersion !== void 0) clean.minWaironVersion = manifest.minWaironVersion;
|
|
132
|
+
if (manifest.digest !== void 0) clean.digest = manifest.digest;
|
|
133
|
+
if (manifest.entryDigests !== void 0) clean.entryDigests = manifest.entryDigests;
|
|
134
|
+
if (manifest.generatedBy !== void 0) clean.generatedBy = manifest.generatedBy;
|
|
135
|
+
if (manifest.generatedAt !== void 0) clean.generatedAt = manifest.generatedAt;
|
|
136
|
+
return (0, import_js_yaml.dump)(clean, { sortKeys: false });
|
|
137
|
+
}
|
|
138
|
+
function defaultLimits() {
|
|
139
|
+
return { ...DEFAULTS };
|
|
140
|
+
}
|
|
141
|
+
function planExtraction(entries, limits) {
|
|
142
|
+
const caps = {
|
|
143
|
+
maxEntries: limits.maxEntries ?? DEFAULTS.maxEntries,
|
|
144
|
+
maxTotalUncompressedBytes: limits.maxTotalUncompressedBytes ?? DEFAULTS.maxTotalUncompressedBytes,
|
|
145
|
+
maxEntryBytes: limits.maxEntryBytes ?? DEFAULTS.maxEntryBytes,
|
|
146
|
+
maxCompressionRatio: limits.maxCompressionRatio ?? DEFAULTS.maxCompressionRatio,
|
|
147
|
+
maxDepth: limits.maxDepth ?? DEFAULTS.maxDepth
|
|
148
|
+
};
|
|
149
|
+
const approved = [];
|
|
150
|
+
let total = 0;
|
|
151
|
+
for (const entry of entries) {
|
|
152
|
+
if (isSymlinkOrNonRegular(entry.kind) || isStructurallyUnsafePath(entry.path)) {
|
|
153
|
+
rejectUnsafe();
|
|
154
|
+
}
|
|
155
|
+
if (entry.kind === "dir") continue;
|
|
156
|
+
const normalized = normalizeRelPath(entry.path);
|
|
157
|
+
if (escapesDestination(normalized) || pathDepth(normalized) > caps.maxDepth) {
|
|
158
|
+
rejectUnsafe();
|
|
159
|
+
}
|
|
160
|
+
if (entry.uncompressedSize > caps.maxEntryBytes || compressionRatio(entry) > caps.maxCompressionRatio) {
|
|
161
|
+
rejectUnsafe();
|
|
162
|
+
}
|
|
163
|
+
approved.push(normalized);
|
|
164
|
+
total += entry.uncompressedSize;
|
|
165
|
+
}
|
|
166
|
+
if (approved.length > caps.maxEntries || total > caps.maxTotalUncompressedBytes) {
|
|
167
|
+
rejectUnsafe();
|
|
168
|
+
}
|
|
169
|
+
return { paths: approved, totalUncompressedBytes: total };
|
|
170
|
+
}
|
|
171
|
+
function verifyIdentity(manifest, packName, packVersion) {
|
|
172
|
+
if (manifest.name !== packName || manifest.version !== packVersion) {
|
|
173
|
+
throw new Error("envelope identity mismatch (envelope name/version differ from the inner pack manifest)");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function verifyIntegrity(manifest, files) {
|
|
177
|
+
if (!manifest.entryDigests) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
const digests = manifest.entryDigests;
|
|
181
|
+
for (const file of files) {
|
|
182
|
+
if (file.path === ENVELOPE_FILENAME) continue;
|
|
183
|
+
if (sha256(file.contents) !== digests[file.path]) {
|
|
184
|
+
throw new Error("integrity digest mismatch");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
function sealIntegrity(manifest, files, generatedBy, generatedAt) {
|
|
190
|
+
const entryDigests = {};
|
|
191
|
+
for (const file of files) {
|
|
192
|
+
if (file.path === ENVELOPE_FILENAME) continue;
|
|
193
|
+
entryDigests[file.path] = sha256(file.contents);
|
|
194
|
+
}
|
|
195
|
+
const digest = sha256(canonicalDigestBytes(entryDigests));
|
|
196
|
+
return { ...manifest, entryDigests, digest, generatedBy, generatedAt };
|
|
197
|
+
}
|
|
198
|
+
function checkCompatibility(manifest, waironVersion) {
|
|
199
|
+
if (Math.trunc(manifest.formatVersion) > SUPPORTED_FORMAT_VERSION) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
if (manifest.minWaironVersion && isBelow(waironVersion, manifest.minWaironVersion)) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
function rejectUnsafe() {
|
|
208
|
+
throw new Error("reject: unsafe or oversized archive entry (zip-slip / size / ratio / depth guard)");
|
|
209
|
+
}
|
|
210
|
+
function validateEnvelope(raw) {
|
|
211
|
+
if (!raw || typeof raw !== "object") {
|
|
212
|
+
throw new Error("malformed pack envelope (not a YAML mapping)");
|
|
213
|
+
}
|
|
214
|
+
const o = raw;
|
|
215
|
+
if (typeof o.formatVersion !== "number") throw new Error("pack envelope: formatVersion must be a number");
|
|
216
|
+
if (typeof o.name !== "string" || !o.name) throw new Error("pack envelope: name is required");
|
|
217
|
+
if (typeof o.version !== "string" || !o.version) throw new Error("pack envelope: version is required");
|
|
218
|
+
if (typeof o.kind !== "string" || o.kind !== "declarative" && o.kind !== "code") {
|
|
219
|
+
throw new Error('pack envelope: kind must be "declarative" or "code"');
|
|
220
|
+
}
|
|
221
|
+
if (typeof o.entry !== "string" || !o.entry) throw new Error("pack envelope: entry is required");
|
|
222
|
+
const manifest = {
|
|
223
|
+
formatVersion: o.formatVersion,
|
|
224
|
+
name: o.name,
|
|
225
|
+
version: o.version,
|
|
226
|
+
kind: o.kind,
|
|
227
|
+
entry: o.entry
|
|
228
|
+
};
|
|
229
|
+
if (typeof o.minWaironVersion === "string") manifest.minWaironVersion = o.minWaironVersion;
|
|
230
|
+
if (typeof o.digest === "string") manifest.digest = o.digest;
|
|
231
|
+
if (o.entryDigests && typeof o.entryDigests === "object") {
|
|
232
|
+
manifest.entryDigests = o.entryDigests;
|
|
233
|
+
}
|
|
234
|
+
if (typeof o.generatedBy === "string") manifest.generatedBy = o.generatedBy;
|
|
235
|
+
if (typeof o.generatedAt === "string") manifest.generatedAt = o.generatedAt;
|
|
236
|
+
return manifest;
|
|
237
|
+
}
|
|
238
|
+
function isSymlinkOrNonRegular(kind) {
|
|
239
|
+
return kind !== "file" && kind !== "dir";
|
|
240
|
+
}
|
|
241
|
+
function isStructurallyUnsafePath(p) {
|
|
242
|
+
if (p.includes("\\")) return true;
|
|
243
|
+
if (/^[A-Za-z]:/.test(p)) return true;
|
|
244
|
+
if (p.startsWith("/")) return true;
|
|
245
|
+
return p.split("/").some((seg) => seg === "..");
|
|
246
|
+
}
|
|
247
|
+
function normalizeRelPath(p) {
|
|
248
|
+
return p.split("/").filter((seg) => seg !== "" && seg !== ".").join("/");
|
|
249
|
+
}
|
|
250
|
+
function escapesDestination(normalized) {
|
|
251
|
+
return normalized.startsWith("/") || normalized.split("/").some((seg) => seg === "..");
|
|
252
|
+
}
|
|
253
|
+
function pathDepth(normalized) {
|
|
254
|
+
return normalized.split("/").filter((seg) => seg !== "").length;
|
|
255
|
+
}
|
|
256
|
+
function compressionRatio(entry) {
|
|
257
|
+
if (entry.compressedSize > 0) return entry.uncompressedSize / entry.compressedSize;
|
|
258
|
+
return entry.uncompressedSize > 0 ? Infinity : 0;
|
|
259
|
+
}
|
|
260
|
+
function canonicalDigestBytes(entryDigests) {
|
|
261
|
+
const lines = Object.keys(entryDigests).sort().map((key) => `${key}:${entryDigests[key]}`).join("\n");
|
|
262
|
+
return new TextEncoder().encode(lines);
|
|
263
|
+
}
|
|
264
|
+
function sha256(data) {
|
|
265
|
+
return (0, import_crypto.createHash)("sha256").update(data).digest("hex");
|
|
266
|
+
}
|
|
267
|
+
function isBelow(running, minimum) {
|
|
268
|
+
const [rMaj, rMin, rPat] = parseSemverCore(running);
|
|
269
|
+
const [mMaj, mMin, mPat] = parseSemverCore(minimum);
|
|
270
|
+
if (rMaj !== mMaj) return rMaj < mMaj;
|
|
271
|
+
if (rMin !== mMin) return rMin < mMin;
|
|
272
|
+
return rPat < mPat;
|
|
273
|
+
}
|
|
274
|
+
function parseSemverCore(v) {
|
|
275
|
+
const cleaned = v.trim().replace(/^[^0-9]*/, "");
|
|
276
|
+
const core = cleaned.split(/[-+]/)[0];
|
|
277
|
+
const parts = core.split(".").map((n) => parseInt(n, 10));
|
|
278
|
+
return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/version.ts
|
|
282
|
+
var SDK_VERSION = loadVersion();
|
|
283
|
+
function loadVersion() {
|
|
284
|
+
try {
|
|
285
|
+
const pkg = require_package();
|
|
286
|
+
return pkg.version ?? "0.0.0";
|
|
287
|
+
} catch {
|
|
288
|
+
return "0.0.0";
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/scaffold.ts
|
|
293
|
+
var enc = (s) => new TextEncoder().encode(s);
|
|
294
|
+
function render(request) {
|
|
295
|
+
const version = request.version ?? "0.1.0";
|
|
296
|
+
let files;
|
|
297
|
+
if (request.kind === "code") {
|
|
298
|
+
files = renderCodePack(request.name, version);
|
|
299
|
+
} else {
|
|
300
|
+
files = renderDeclarativePack(request.name, version);
|
|
301
|
+
}
|
|
302
|
+
if (request.withSkill) {
|
|
303
|
+
files.push(renderSkillStub(request.name, version));
|
|
304
|
+
}
|
|
305
|
+
return files;
|
|
306
|
+
}
|
|
307
|
+
function renderDeclarativePack(name, version) {
|
|
308
|
+
return [
|
|
309
|
+
{ path: "wairon-pack.yaml", contents: enc(declarativeEnvelope(name, version)) },
|
|
310
|
+
{ path: "pack.yaml", contents: enc(declarativeManifest(name, version)) },
|
|
311
|
+
{ path: "README.md", contents: enc(readme(name, "declarative", "pack.yaml")) }
|
|
312
|
+
];
|
|
313
|
+
}
|
|
314
|
+
function renderCodePack(name, version) {
|
|
315
|
+
return [
|
|
316
|
+
{ path: "wairon-pack.yaml", contents: enc(codeEnvelope(name, version)) },
|
|
317
|
+
{ path: "package.json", contents: enc(codePackageJson(name, version)) },
|
|
318
|
+
{ path: "tsconfig.json", contents: enc(codeTsconfig()) },
|
|
319
|
+
{ path: "pack.ts", contents: enc(codeEntry(name, version)) },
|
|
320
|
+
{ path: "README.md", contents: enc(readme(name, "code", "pack.cjs")) }
|
|
321
|
+
];
|
|
322
|
+
}
|
|
323
|
+
function renderSkillStub(name, _version) {
|
|
324
|
+
const id = slug(name);
|
|
325
|
+
return { path: `skills/${id}/SKILL.md`, contents: enc(skillStub(name)) };
|
|
326
|
+
}
|
|
327
|
+
function declarativeEnvelope(name, version) {
|
|
328
|
+
return [
|
|
329
|
+
"formatVersion: 1",
|
|
330
|
+
`name: ${name}`,
|
|
331
|
+
`version: ${version}`,
|
|
332
|
+
"kind: declarative",
|
|
333
|
+
"entry: pack.yaml",
|
|
334
|
+
""
|
|
335
|
+
].join("\n");
|
|
336
|
+
}
|
|
337
|
+
function codeEnvelope(name, version) {
|
|
338
|
+
return [
|
|
339
|
+
"formatVersion: 1",
|
|
340
|
+
`name: ${name}`,
|
|
341
|
+
`version: ${version}`,
|
|
342
|
+
"kind: code",
|
|
343
|
+
"entry: pack.cjs",
|
|
344
|
+
`minWaironVersion: ${SDK_VERSION}`,
|
|
345
|
+
""
|
|
346
|
+
].join("\n");
|
|
347
|
+
}
|
|
348
|
+
function declarativeManifest(name, version) {
|
|
349
|
+
return [
|
|
350
|
+
`name: ${name}`,
|
|
351
|
+
`version: ${version}`,
|
|
352
|
+
"",
|
|
353
|
+
"# Custom architectural profiles this pack contributes (example, commented):",
|
|
354
|
+
"# profiles:",
|
|
355
|
+
"# my-profile:",
|
|
356
|
+
"# family: backend-like",
|
|
357
|
+
"# forbiddenStereotypes:",
|
|
358
|
+
"# - types: [Adapter]",
|
|
359
|
+
'# reason: "Domain components must not touch adapters directly."',
|
|
360
|
+
"profiles: {}",
|
|
361
|
+
"",
|
|
362
|
+
"# Target language / platform tables (example, commented):",
|
|
363
|
+
"# languages:",
|
|
364
|
+
"# rust:",
|
|
365
|
+
"# unsupportedFlow: {}",
|
|
366
|
+
"# foreignBuiltins: []",
|
|
367
|
+
"languages: {}",
|
|
368
|
+
"",
|
|
369
|
+
"# Reusable, versioned architecture patterns:",
|
|
370
|
+
"patterns: []",
|
|
371
|
+
""
|
|
372
|
+
].join("\n");
|
|
373
|
+
}
|
|
374
|
+
function codePackageJson(name, version) {
|
|
375
|
+
const pkg = {
|
|
376
|
+
name,
|
|
377
|
+
version,
|
|
378
|
+
private: true,
|
|
379
|
+
scripts: {
|
|
380
|
+
build: "esbuild pack.ts --bundle --platform=node --packages=external --format=cjs --outfile=pack.cjs"
|
|
381
|
+
},
|
|
382
|
+
dependencies: {
|
|
383
|
+
"@wairon/sdk": SDK_VERSION
|
|
384
|
+
},
|
|
385
|
+
devDependencies: {
|
|
386
|
+
esbuild: "^0.21.0",
|
|
387
|
+
typescript: "^5.0.0"
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
return `${JSON.stringify(pkg, null, 2)}
|
|
391
|
+
`;
|
|
392
|
+
}
|
|
393
|
+
function codeTsconfig() {
|
|
394
|
+
const tsconfig = {
|
|
395
|
+
compilerOptions: {
|
|
396
|
+
target: "ES2020",
|
|
397
|
+
module: "CommonJS",
|
|
398
|
+
moduleResolution: "node",
|
|
399
|
+
strict: true,
|
|
400
|
+
esModuleInterop: true,
|
|
401
|
+
skipLibCheck: true,
|
|
402
|
+
declaration: false,
|
|
403
|
+
noEmit: true
|
|
404
|
+
},
|
|
405
|
+
include: ["pack.ts"]
|
|
406
|
+
};
|
|
407
|
+
return `${JSON.stringify(tsconfig, null, 2)}
|
|
408
|
+
`;
|
|
409
|
+
}
|
|
410
|
+
function codeEntry(name, version) {
|
|
411
|
+
const code = slug(name).toUpperCase().replace(/-/g, "_");
|
|
412
|
+
return [
|
|
413
|
+
"import { defineRule } from '@wairon/sdk';",
|
|
414
|
+
"import type { RuleContext, Finding } from '@wairon/sdk';",
|
|
415
|
+
"",
|
|
416
|
+
"const exampleRule = defineRule({",
|
|
417
|
+
` name: '${slug(name)}-example',`,
|
|
418
|
+
" description: 'Example architectural rule. Replace with your own doctrine.',",
|
|
419
|
+
` codes: ['${code}_EXAMPLE'],`,
|
|
420
|
+
" check(ctx: RuleContext): Finding[] {",
|
|
421
|
+
" const findings: Finding[] = [];",
|
|
422
|
+
" for (const component of ctx.components) {",
|
|
423
|
+
" // Inspect the spec tree and push findings as needed.",
|
|
424
|
+
" void component;",
|
|
425
|
+
" }",
|
|
426
|
+
" return findings;",
|
|
427
|
+
" },",
|
|
428
|
+
"});",
|
|
429
|
+
"",
|
|
430
|
+
"export default {",
|
|
431
|
+
` name: '${name}',`,
|
|
432
|
+
` version: '${version}',`,
|
|
433
|
+
" rules: [exampleRule],",
|
|
434
|
+
"};",
|
|
435
|
+
""
|
|
436
|
+
].join("\n");
|
|
437
|
+
}
|
|
438
|
+
function readme(name, kind, entry) {
|
|
439
|
+
return [
|
|
440
|
+
`# ${name}`,
|
|
441
|
+
"",
|
|
442
|
+
`A wairon ${kind} pack.`,
|
|
443
|
+
"",
|
|
444
|
+
`- Envelope: \`wairon-pack.yaml\``,
|
|
445
|
+
`- Entry: \`${entry}\``,
|
|
446
|
+
"",
|
|
447
|
+
"Build an installable `.wpack` archive with `wairon pack build`.",
|
|
448
|
+
""
|
|
449
|
+
].join("\n");
|
|
450
|
+
}
|
|
451
|
+
function skillStub(name) {
|
|
452
|
+
return [
|
|
453
|
+
"---",
|
|
454
|
+
`name: ${name}`,
|
|
455
|
+
`description: ${name} authoring skill (stub).`,
|
|
456
|
+
"---",
|
|
457
|
+
"",
|
|
458
|
+
`# ${name}`,
|
|
459
|
+
"",
|
|
460
|
+
"Describe when this skill applies and what it guides.",
|
|
461
|
+
""
|
|
462
|
+
].join("\n");
|
|
463
|
+
}
|
|
464
|
+
function slug(name) {
|
|
465
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/archive.ts
|
|
469
|
+
var fs = __toESM(require("fs"));
|
|
470
|
+
var path = __toESM(require("path"));
|
|
471
|
+
var import_fflate = require("fflate");
|
|
472
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".svn"]);
|
|
473
|
+
function listEntries(archive) {
|
|
474
|
+
const symlinks = detectSymlinkNames(archive);
|
|
475
|
+
const metas = [];
|
|
476
|
+
(0, import_fflate.unzipSync)(archive, {
|
|
477
|
+
filter: (info) => {
|
|
478
|
+
metas.push({
|
|
479
|
+
path: info.name,
|
|
480
|
+
uncompressedSize: info.originalSize,
|
|
481
|
+
compressedSize: info.size,
|
|
482
|
+
kind: classifyKind(info.name, symlinks)
|
|
483
|
+
});
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
return metas;
|
|
488
|
+
}
|
|
489
|
+
function inflateEntry(archive, entryPath) {
|
|
490
|
+
const inflated = (0, import_fflate.unzipSync)(archive, { filter: (info) => info.name === entryPath });
|
|
491
|
+
const bytes = inflated[entryPath];
|
|
492
|
+
if (!bytes) throw new Error(`archive entry not found: ${entryPath}`);
|
|
493
|
+
return bytes;
|
|
494
|
+
}
|
|
495
|
+
function assembleArchive(files) {
|
|
496
|
+
const zippable = {};
|
|
497
|
+
for (const file of files) zippable[file.path] = file.contents;
|
|
498
|
+
const bytes = (0, import_fflate.zipSync)(zippable);
|
|
499
|
+
return bytes;
|
|
500
|
+
}
|
|
501
|
+
function readPackDir(dir) {
|
|
502
|
+
const files = [];
|
|
503
|
+
walkPackDir(dir, dir, files);
|
|
504
|
+
return files;
|
|
505
|
+
}
|
|
506
|
+
function writeTree(destDir, files) {
|
|
507
|
+
for (const file of files) {
|
|
508
|
+
const absolute = path.join(destDir, file.path);
|
|
509
|
+
fs.mkdirSync(path.dirname(absolute), { recursive: true });
|
|
510
|
+
fs.writeFileSync(absolute, file.contents);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function classifyKind(name, symlinks) {
|
|
514
|
+
if (symlinks.has(name)) return "symlink";
|
|
515
|
+
if (name.endsWith("/")) return "dir";
|
|
516
|
+
return "file";
|
|
517
|
+
}
|
|
518
|
+
function walkPackDir(root, current, out) {
|
|
519
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
520
|
+
if (entry.isDirectory()) {
|
|
521
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
522
|
+
walkPackDir(root, path.join(current, entry.name), out);
|
|
523
|
+
} else if (entry.isFile()) {
|
|
524
|
+
const absolute = path.join(current, entry.name);
|
|
525
|
+
const relative2 = path.relative(root, absolute).split(path.sep).join("/");
|
|
526
|
+
out.push({ path: relative2, contents: fs.readFileSync(absolute) });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function detectSymlinkNames(archive) {
|
|
531
|
+
const names = /* @__PURE__ */ new Set();
|
|
532
|
+
try {
|
|
533
|
+
const eocd = findEocd(archive);
|
|
534
|
+
if (eocd < 0) return names;
|
|
535
|
+
let p = readU32(archive, eocd + 16);
|
|
536
|
+
const count = readU16(archive, eocd + 10);
|
|
537
|
+
const decoder = new TextDecoder();
|
|
538
|
+
for (let i = 0; i < count; i++) {
|
|
539
|
+
if (readU32(archive, p) !== 33639248) break;
|
|
540
|
+
const versionMadeBy = readU16(archive, p + 4);
|
|
541
|
+
const nameLen = readU16(archive, p + 28);
|
|
542
|
+
const extraLen = readU16(archive, p + 30);
|
|
543
|
+
const commentLen = readU16(archive, p + 32);
|
|
544
|
+
const externalAttrs = readU32(archive, p + 38);
|
|
545
|
+
const name = decoder.decode(archive.subarray(p + 46, p + 46 + nameLen));
|
|
546
|
+
if (versionMadeBy >> 8 === 3) {
|
|
547
|
+
const unixMode = externalAttrs >>> 16 & 65535;
|
|
548
|
+
if ((unixMode & 61440) === 40960) names.add(name);
|
|
549
|
+
}
|
|
550
|
+
p += 46 + nameLen + extraLen + commentLen;
|
|
551
|
+
}
|
|
552
|
+
} catch {
|
|
553
|
+
return names;
|
|
554
|
+
}
|
|
555
|
+
return names;
|
|
556
|
+
}
|
|
557
|
+
function findEocd(buf) {
|
|
558
|
+
const minRecord = 22;
|
|
559
|
+
const lowerBound = Math.max(0, buf.length - (minRecord + 65535));
|
|
560
|
+
for (let i = buf.length - minRecord; i >= lowerBound; i--) {
|
|
561
|
+
if (readU32(buf, i) === 101010256) return i;
|
|
562
|
+
}
|
|
563
|
+
return -1;
|
|
564
|
+
}
|
|
565
|
+
function readU16(buf, off) {
|
|
566
|
+
return buf[off] | buf[off + 1] << 8;
|
|
567
|
+
}
|
|
568
|
+
function readU32(buf, off) {
|
|
569
|
+
return (buf[off] | buf[off + 1] << 8 | buf[off + 2] << 16 | buf[off + 3] << 24) >>> 0;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// src/orchestrator.ts
|
|
573
|
+
var decode = (b) => new TextDecoder().decode(b);
|
|
574
|
+
var encode = (s) => new TextEncoder().encode(s);
|
|
575
|
+
function scaffoldPack(request) {
|
|
576
|
+
const files = render(request);
|
|
577
|
+
writeTree(request.targetDir, files);
|
|
578
|
+
return files.map((file) => path2.join(request.targetDir, file.path));
|
|
579
|
+
}
|
|
580
|
+
function buildPack(sourceDir) {
|
|
581
|
+
const files = readPackDir(sourceDir);
|
|
582
|
+
const envelope = files.find((file) => file.path === ENVELOPE_FILENAME);
|
|
583
|
+
if (!envelope) {
|
|
584
|
+
throw new Error("pack build requires a wairon-pack.yaml envelope (run `wairon pack init` to scaffold one)");
|
|
585
|
+
}
|
|
586
|
+
const manifest = parseManifest(decode(envelope.contents));
|
|
587
|
+
const identity = readInnerIdentity(files, manifest);
|
|
588
|
+
verifyIdentity(manifest, identity.name, identity.version);
|
|
589
|
+
const sealed = sealIntegrity(manifest, files, `@wairon/sdk@${SDK_VERSION}`, (/* @__PURE__ */ new Date()).toISOString());
|
|
590
|
+
const sealedText = serializeManifest(sealed);
|
|
591
|
+
const finalFiles = upsertEnvelope(files, sealedText);
|
|
592
|
+
const archiveBytes = assembleArchive(finalFiles);
|
|
593
|
+
const info = buildInfo(sealed, finalFiles);
|
|
594
|
+
const suggestedFileName = `${sealed.name}-${sealed.version}.wpack`;
|
|
595
|
+
return { archive: archiveBytes, info, suggestedFileName };
|
|
596
|
+
}
|
|
597
|
+
function inspectArchive(archiveBytes) {
|
|
598
|
+
const entries = listEntries(archiveBytes);
|
|
599
|
+
if (!entries.some((entry) => entry.path === ENVELOPE_FILENAME)) {
|
|
600
|
+
throw new Error("not a .wpack archive (missing wairon-pack.yaml envelope)");
|
|
601
|
+
}
|
|
602
|
+
const envelopeBytes = inflateEntry(archiveBytes, ENVELOPE_FILENAME);
|
|
603
|
+
const manifest = parseManifest(decode(envelopeBytes));
|
|
604
|
+
const compatible = checkCompatibility(manifest, SDK_VERSION);
|
|
605
|
+
const fileEntries = entries.filter((entry) => entry.kind === "file");
|
|
606
|
+
const totalUncompressedBytes = entries.reduce((sum, entry) => sum + entry.uncompressedSize, 0);
|
|
607
|
+
const info = {
|
|
608
|
+
name: manifest.name,
|
|
609
|
+
version: manifest.version,
|
|
610
|
+
kind: manifest.kind,
|
|
611
|
+
entry: manifest.entry,
|
|
612
|
+
formatVersion: manifest.formatVersion,
|
|
613
|
+
entryCount: fileEntries.length,
|
|
614
|
+
totalUncompressedBytes,
|
|
615
|
+
compatible
|
|
616
|
+
};
|
|
617
|
+
if (manifest.minWaironVersion !== void 0) info.minWaironVersion = manifest.minWaironVersion;
|
|
618
|
+
return info;
|
|
619
|
+
}
|
|
620
|
+
function extractPack(archiveBytes, destDir, limits) {
|
|
621
|
+
const effectiveLimits = limits === void 0 ? defaultLimits() : limits;
|
|
622
|
+
const entries = listEntries(archiveBytes);
|
|
623
|
+
const plan = planExtraction(entries, effectiveLimits);
|
|
624
|
+
const files = [];
|
|
625
|
+
for (const approvedPath of plan.paths) {
|
|
626
|
+
const contents = inflateEntry(archiveBytes, approvedPath);
|
|
627
|
+
files.push({ path: approvedPath, contents });
|
|
628
|
+
}
|
|
629
|
+
const envelope = files.find((file) => file.path === ENVELOPE_FILENAME);
|
|
630
|
+
if (!envelope) {
|
|
631
|
+
throw new Error("not a .wpack archive (missing wairon-pack.yaml envelope)");
|
|
632
|
+
}
|
|
633
|
+
const manifest = parseManifest(decode(envelope.contents));
|
|
634
|
+
verifyIntegrity(manifest, files);
|
|
635
|
+
const directory = path2.resolve(destDir);
|
|
636
|
+
writeTree(directory, files);
|
|
637
|
+
const result = {
|
|
638
|
+
directory,
|
|
639
|
+
entryPath: manifest.entry,
|
|
640
|
+
manifest,
|
|
641
|
+
name: manifest.name,
|
|
642
|
+
kind: manifest.kind,
|
|
643
|
+
entryCount: files.length
|
|
644
|
+
};
|
|
645
|
+
return result;
|
|
646
|
+
}
|
|
647
|
+
function readInnerIdentity(files, manifest) {
|
|
648
|
+
const innerPath = manifest.kind === "code" ? "package.json" : manifest.entry;
|
|
649
|
+
const inner = files.find((file) => file.path === innerPath);
|
|
650
|
+
if (!inner) throw new Error(`pack build: inner pack manifest "${innerPath}" is missing`);
|
|
651
|
+
const parsed = manifest.kind === "code" ? JSON.parse(decode(inner.contents)) : (0, import_js_yaml2.load)(decode(inner.contents));
|
|
652
|
+
const name = parsed?.name;
|
|
653
|
+
const version = parsed?.version;
|
|
654
|
+
if (typeof name !== "string" || typeof version !== "string") {
|
|
655
|
+
throw new Error(`pack build: inner pack manifest "${innerPath}" lacks a name/version`);
|
|
656
|
+
}
|
|
657
|
+
return { name, version };
|
|
658
|
+
}
|
|
659
|
+
function upsertEnvelope(files, envelopeText) {
|
|
660
|
+
const others = files.filter((file) => file.path !== ENVELOPE_FILENAME);
|
|
661
|
+
return [...others, { path: ENVELOPE_FILENAME, contents: encode(envelopeText) }];
|
|
662
|
+
}
|
|
663
|
+
function buildInfo(manifest, files) {
|
|
664
|
+
const totalUncompressedBytes = files.reduce((sum, file) => sum + file.contents.length, 0);
|
|
665
|
+
const info = {
|
|
666
|
+
name: manifest.name,
|
|
667
|
+
version: manifest.version,
|
|
668
|
+
kind: manifest.kind,
|
|
669
|
+
entry: manifest.entry,
|
|
670
|
+
formatVersion: manifest.formatVersion,
|
|
671
|
+
entryCount: files.length,
|
|
672
|
+
totalUncompressedBytes,
|
|
673
|
+
compatible: true
|
|
674
|
+
};
|
|
675
|
+
if (manifest.minWaironVersion !== void 0) info.minWaironVersion = manifest.minWaironVersion;
|
|
676
|
+
return info;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/authoring.ts
|
|
680
|
+
function defineRule(rule) {
|
|
681
|
+
return rule;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// src/index.ts
|
|
685
|
+
function scaffoldPack2(request) {
|
|
686
|
+
return scaffoldPack(request);
|
|
687
|
+
}
|
|
688
|
+
function buildPack2(sourceDir) {
|
|
689
|
+
return buildPack(sourceDir);
|
|
690
|
+
}
|
|
691
|
+
function inspectArchive2(archive) {
|
|
692
|
+
return inspectArchive(archive);
|
|
693
|
+
}
|
|
694
|
+
function extractPack2(archive, destDir, limits) {
|
|
695
|
+
return extractPack(archive, destDir, limits);
|
|
696
|
+
}
|
|
697
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
698
|
+
0 && (module.exports = {
|
|
699
|
+
buildPack,
|
|
700
|
+
defineRule,
|
|
701
|
+
extractPack,
|
|
702
|
+
inspectArchive,
|
|
703
|
+
scaffoldPack
|
|
704
|
+
});
|
|
705
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../package.json","../src/index.ts","../src/orchestrator.ts","../src/codec.ts","../src/version.ts","../src/scaffold.ts","../src/archive.ts","../src/authoring.ts"],"sourcesContent":["{\n \"name\": \"@wairon/sdk\",\n \"version\": \"5.0.1-dev.5\",\n \"description\": \"Wairon pack-archive (.wpack) format authority and authoring toolkit — scaffold, build, inspect, and safely extract wairon extension packs.\",\n \"keywords\": [\n \"wairon\",\n \"sdd\",\n \"pack\",\n \"wpack\",\n \"archive\",\n \"developer-tools\"\n ],\n \"author\": \"SYW\",\n \"license\": \"MIT\",\n \"homepage\": \"https://github.com/SYW-Apps/Waffle-AIron\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/SYW-Apps/Waffle-AIron.git\"\n },\n \"type\": \"commonjs\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"clean\": \"rimraf dist\"\n },\n \"dependencies\": {\n \"fflate\": \"^0.8.2\",\n \"js-yaml\": \"^4.1.0\"\n },\n \"devDependencies\": {\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^20.14.0\",\n \"rimraf\": \"^5.0.7\",\n \"tsup\": \"^8.1.0\",\n \"typescript\": \"^5.5.2\",\n \"vitest\": \"^4.1.9\"\n }\n}\n","import * as orchestrator from './orchestrator.js';\nimport type {\n PackArchiveInfo,\n PackBuildResult,\n PackExtractionLimits,\n PackExtractionResult,\n PackScaffoldRequest,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// SDK Portal (sdk_portal_impl) — the @wairon/sdk package entry. Each capability\n// is a 1:1 forward to the SDK orchestrator with no logic of its own; the module\n// also re-exports the pure pack-authoring value objects and the rule-authoring\n// contract (compile-time types + the defineRule helper).\n// ---------------------------------------------------------------------------\n\n/** Scaffold a new pack project (declarative or code) into a directory; returns the created file paths. */\nexport function scaffoldPack(request: PackScaffoldRequest): string[] {\n // Step 1: forward to the SDK orchestrator.\n return orchestrator.scaffoldPack(request);\n}\n\n/** Build an installable .wpack archive from a pack directory. */\nexport function buildPack(sourceDir: string): PackBuildResult {\n // Step 1: forward to the SDK orchestrator.\n return orchestrator.buildPack(sourceDir);\n}\n\n/** Inspect + verify a .wpack archive without extracting it. */\nexport function inspectArchive(archive: Uint8Array): PackArchiveInfo {\n // Step 1: forward to the SDK orchestrator.\n return orchestrator.inspectArchive(archive);\n}\n\n/** Safely extract a .wpack archive into a destination directory under enforced limits. */\nexport function extractPack(\n archive: Uint8Array,\n destDir: string,\n limits?: PackExtractionLimits,\n): PackExtractionResult {\n // Step 1: forward to the SDK orchestrator.\n return orchestrator.extractPack(archive, destDir, limits);\n}\n\n// Re-export the pure pack-archive value objects...\nexport * from './types.js';\n// ...and the rule-authoring contract (SddRule + stable RuleContext/Finding facade + defineRule).\nexport * from './authoring.js';\n","import * as path from 'path';\nimport { load as yamlLoad } from 'js-yaml';\nimport * as codec from './codec.js';\nimport * as scaffold from './scaffold.js';\nimport * as archive from './archive.js';\nimport { SDK_VERSION } from './version.js';\nimport type {\n PackArchiveInfo,\n PackArchiveManifest,\n PackBuildResult,\n PackExtractionLimits,\n PackExtractionResult,\n PackFile,\n PackScaffoldRequest,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// SDK Orchestrator (sdk_orchestrator_impl) — the four pack workflows. Delegates\n// format/safety decisions to the codec, template rendering to the scaffold\n// specialist, and all zip+fs I/O to the archive adapter. Holds no state.\n// ---------------------------------------------------------------------------\n\nconst decode = (b: Uint8Array): string => new TextDecoder().decode(b);\nconst encode = (s: string): Uint8Array => new TextEncoder().encode(s);\n\n/** Render the scaffold file map and write it under the target directory. */\nexport function scaffoldPack(request: PackScaffoldRequest): string[] {\n // Step 1: render the scaffold file map for the requested pack.\n const files = scaffold.render(request);\n // Step 2: write the rendered files as a directory tree under targetDir.\n archive.writeTree(request.targetDir, files);\n // Step 3: return the created file paths (targetDir-joined).\n return files.map((file) => path.join(request.targetDir, file.path));\n}\n\n/** Build an installable .wpack archive from a pack directory. */\nexport function buildPack(sourceDir: string): PackBuildResult {\n // Step 1: read the source pack directory into a file map.\n const files = archive.readPackDir(sourceDir);\n // Step 2: require an envelope.\n const envelope = files.find((file) => file.path === codec.ENVELOPE_FILENAME);\n if (!envelope) {\n // Step 3: reject a pack directory with no envelope.\n throw new Error('pack build requires a wairon-pack.yaml envelope (run `wairon pack init` to scaffold one)');\n }\n // Step 4 (parse): parse + validate the envelope (also version-gates the format).\n const manifest = codec.parseManifest(decode(envelope.contents));\n // Step 5: read the inner pack identity (pack.yaml, or package.json for code).\n const identity = readInnerIdentity(files, manifest);\n // Step 6: assert the envelope's name/version match the inner pack manifest.\n codec.verifyIdentity(manifest, identity.name, identity.version);\n // Step 7: seal integrity (entryDigests + digest + generatedBy/generatedAt).\n const sealed = codec.sealIntegrity(manifest, files, `@wairon/sdk@${SDK_VERSION}`, new Date().toISOString());\n // Step 8: serialize the sealed manifest back to canonical envelope text.\n const sealedText = codec.serializeManifest(sealed);\n // Step 9: upsert wairon-pack.yaml (sealed envelope text) into the file map.\n const finalFiles = upsertEnvelope(files, sealedText);\n // Step 10: deflate the files + envelope into .wpack archive bytes.\n const archiveBytes = archive.assembleArchive(finalFiles);\n // Step 11: assemble PackArchiveInfo + suggestedFileName.\n const info = buildInfo(sealed, finalFiles);\n const suggestedFileName = `${sealed.name}-${sealed.version}.wpack`;\n // Step 12: return the build result.\n return { archive: archiveBytes, info, suggestedFileName };\n}\n\n/** Inspect + verify a .wpack archive without extracting it. */\nexport function inspectArchive(archiveBytes: Uint8Array): PackArchiveInfo {\n // Step 1: enumerate archive entries without inflating.\n const entries = archive.listEntries(archiveBytes);\n // Step 2: is there no wairon-pack.yaml entry?\n if (!entries.some((entry) => entry.path === codec.ENVELOPE_FILENAME)) {\n // Step 3: reject a non-.wpack archive.\n throw new Error('not a .wpack archive (missing wairon-pack.yaml envelope)');\n }\n // Step 4 (readEnv): inflate just the envelope entry.\n const envelopeBytes = archive.inflateEntry(archiveBytes, codec.ENVELOPE_FILENAME);\n // Step 5: parse + version-check the envelope text.\n const manifest = codec.parseManifest(decode(envelopeBytes));\n // Step 6: compute compatibility against the running wairon version.\n const compatible = codec.checkCompatibility(manifest, SDK_VERSION);\n // Step 7: assemble PackArchiveInfo from the manifest + archive stats.\n const fileEntries = entries.filter((entry) => entry.kind === 'file');\n const totalUncompressedBytes = entries.reduce((sum, entry) => sum + entry.uncompressedSize, 0);\n const info: PackArchiveInfo = {\n name: manifest.name,\n version: manifest.version,\n kind: manifest.kind,\n entry: manifest.entry,\n formatVersion: manifest.formatVersion,\n entryCount: fileEntries.length,\n totalUncompressedBytes,\n compatible,\n };\n if (manifest.minWaironVersion !== undefined) info.minWaironVersion = manifest.minWaironVersion;\n // Step 8: return the PackArchiveInfo (no extraction performed).\n return info;\n}\n\n/** Safely extract a .wpack archive into a destination directory under enforced limits. */\nexport function extractPack(\n archiveBytes: Uint8Array,\n destDir: string,\n limits?: PackExtractionLimits,\n): PackExtractionResult {\n // Step 1: did the caller omit limits?\n const effectiveLimits = limits === undefined\n // Step 2: fall back to the codec's default limit profile.\n ? codec.defaultLimits()\n : limits;\n // Step 3 (list): enumerate archive entries without inflating (caps run pre-decompress).\n const entries = archive.listEntries(archiveBytes);\n // Step 4: compute the safe extraction plan (throws on any unsafe/oversized entry).\n const plan = codec.planExtraction(entries, effectiveLimits);\n // Step 5: initialize an empty file list.\n const files: PackFile[] = [];\n // Step 6: inflate each approved path from the plan.\n for (const approvedPath of plan.paths) {\n // Step 7: inflate one approved entry.\n const contents = archive.inflateEntry(archiveBytes, approvedPath);\n // Step 8 (inflateEnd): append { path, contents } to the file list.\n files.push({ path: approvedPath, contents });\n }\n // Step 9: read the wairon-pack.yaml text from the inflated files.\n const envelope = files.find((file) => file.path === codec.ENVELOPE_FILENAME);\n if (!envelope) {\n throw new Error('not a .wpack archive (missing wairon-pack.yaml envelope)');\n }\n // Step 10: parse the envelope into the result manifest.\n const manifest = codec.parseManifest(decode(envelope.contents));\n // Step 11: enforce integrity BEFORE writing (throws on mismatch; no-op when none carried).\n codec.verifyIntegrity(manifest, files);\n // Step 12: write the approved, integrity-checked files as a directory tree.\n const directory = path.resolve(destDir);\n archive.writeTree(directory, files);\n // Step 13: assemble PackExtractionResult.\n const result: PackExtractionResult = {\n directory,\n entryPath: manifest.entry,\n manifest,\n name: manifest.name,\n kind: manifest.kind,\n entryCount: files.length,\n };\n // Step 14: return where the pack landed + its parsed manifest.\n return result;\n}\n\n// --- helpers ---------------------------------------------------------------\n\nfunction readInnerIdentity(files: PackFile[], manifest: PackArchiveManifest): { name: string; version: string } {\n const innerPath = manifest.kind === 'code' ? 'package.json' : manifest.entry;\n const inner = files.find((file) => file.path === innerPath);\n if (!inner) throw new Error(`pack build: inner pack manifest \"${innerPath}\" is missing`);\n const parsed = (manifest.kind === 'code'\n ? JSON.parse(decode(inner.contents))\n : yamlLoad(decode(inner.contents))) as Record<string, unknown> | null;\n const name = parsed?.name;\n const version = parsed?.version;\n if (typeof name !== 'string' || typeof version !== 'string') {\n throw new Error(`pack build: inner pack manifest \"${innerPath}\" lacks a name/version`);\n }\n return { name, version };\n}\n\nfunction upsertEnvelope(files: PackFile[], envelopeText: string): PackFile[] {\n const others = files.filter((file) => file.path !== codec.ENVELOPE_FILENAME);\n return [...others, { path: codec.ENVELOPE_FILENAME, contents: encode(envelopeText) }];\n}\n\nfunction buildInfo(manifest: PackArchiveManifest, files: PackFile[]): PackArchiveInfo {\n const totalUncompressedBytes = files.reduce((sum, file) => sum + file.contents.length, 0);\n const info: PackArchiveInfo = {\n name: manifest.name,\n version: manifest.version,\n kind: manifest.kind,\n entry: manifest.entry,\n formatVersion: manifest.formatVersion,\n entryCount: files.length,\n totalUncompressedBytes,\n compatible: true,\n };\n if (manifest.minWaironVersion !== undefined) info.minWaironVersion = manifest.minWaironVersion;\n return info;\n}\n","import { load as yamlLoad, dump as yamlDump } from 'js-yaml';\nimport { createHash } from 'crypto';\nimport type {\n ArchiveEntryMeta,\n PackArchiveManifest,\n PackExtractionLimits,\n PackExtractionPlan,\n PackFile,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Pack Codec (pack_codec_impl) — PURE format + safety logic, no I/O.\n//\n// planExtraction is the single safety chokepoint (zip-slip / size / ratio /\n// depth / symlink guards, all pre-decompress). sealIntegrity/verifyIntegrity\n// are the produce/verify integrity pair. The codec stays pure: build time is\n// passed in, never read here.\n// ---------------------------------------------------------------------------\n\n/** The archive-root envelope filename. */\nexport const ENVELOPE_FILENAME = 'wairon-pack.yaml';\n\n/** The highest pack-archive format major this SDK understands. */\nconst SUPPORTED_FORMAT_VERSION = 1;\n\n/** The default extraction-safety profile: 4096 / 32 MiB / 8 MiB / 100:1 / depth 16. */\nconst DEFAULTS = {\n maxEntries: 4096,\n maxTotalUncompressedBytes: 32 * 1024 * 1024,\n maxEntryBytes: 8 * 1024 * 1024,\n maxCompressionRatio: 100,\n maxDepth: 16,\n} as const;\n\n/** Parse + validate the envelope text into a PackArchiveManifest; throws on malformed/newer-major. */\nexport function parseManifest(text: string): PackArchiveManifest {\n // Step 1: parse the envelope YAML text (throws on malformed YAML).\n const raw = yamlLoad(text);\n // Step 2: validate the required fields are present and well-typed.\n const manifest = validateEnvelope(raw);\n // Step 3: is the format major newer than this wairon supports?\n if (Math.trunc(manifest.formatVersion) > SUPPORTED_FORMAT_VERSION) {\n // Step 4: fail loudly rather than silently mis-read a newer format.\n throw new Error('unsupported pack-archive formatVersion (newer than this wairon understands)');\n }\n // Step 5: return the parsed manifest.\n return manifest;\n}\n\n/** Serialize a manifest to canonical wairon-pack.yaml text (build path). */\nexport function serializeManifest(manifest: PackArchiveManifest): string {\n // Step 1: drop undefined optional fields for a clean, canonical envelope.\n const clean: Record<string, unknown> = {\n formatVersion: manifest.formatVersion,\n name: manifest.name,\n version: manifest.version,\n kind: manifest.kind,\n entry: manifest.entry,\n };\n if (manifest.minWaironVersion !== undefined) clean.minWaironVersion = manifest.minWaironVersion;\n if (manifest.digest !== undefined) clean.digest = manifest.digest;\n if (manifest.entryDigests !== undefined) clean.entryDigests = manifest.entryDigests;\n if (manifest.generatedBy !== undefined) clean.generatedBy = manifest.generatedBy;\n if (manifest.generatedAt !== undefined) clean.generatedAt = manifest.generatedAt;\n // Step 2: return the wairon-pack.yaml text.\n return yamlDump(clean, { sortKeys: false });\n}\n\n/** The default extraction-safety profile; callers override individual caps. */\nexport function defaultLimits(): PackExtractionLimits {\n // Step 1: return the default caps profile.\n return { ...DEFAULTS };\n}\n\n/** Compute the safe extraction plan from enumerated entries under `limits`, or throw. */\nexport function planExtraction(entries: ArchiveEntryMeta[], limits: PackExtractionLimits): PackExtractionPlan {\n // Step 1: resolve effective caps — each field of `limits` over the defaults.\n const caps = {\n maxEntries: limits.maxEntries ?? DEFAULTS.maxEntries,\n maxTotalUncompressedBytes: limits.maxTotalUncompressedBytes ?? DEFAULTS.maxTotalUncompressedBytes,\n maxEntryBytes: limits.maxEntryBytes ?? DEFAULTS.maxEntryBytes,\n maxCompressionRatio: limits.maxCompressionRatio ?? DEFAULTS.maxCompressionRatio,\n maxDepth: limits.maxDepth ?? DEFAULTS.maxDepth,\n };\n // Step 2: initialize an empty approved-path list and a running total of 0.\n const approved: string[] = [];\n let total = 0;\n // Step 3: vet each enumerated entry.\n for (const entry of entries) {\n // Step 4: reject symlinks/non-regular entries and structurally unsafe paths.\n if (isSymlinkOrNonRegular(entry.kind) || isStructurallyUnsafePath(entry.path)) {\n rejectUnsafe();\n }\n // Directory entries are structural (writeTree recreates them from file\n // paths) — skip without approving, never inflate a 0-byte dir marker.\n if (entry.kind === 'dir') continue;\n // Step 5: normalize the entry path to a destination-relative POSIX path.\n const normalized = normalizeRelPath(entry.path);\n // Step 6: reject zip-slip and over-deep paths.\n if (escapesDestination(normalized) || pathDepth(normalized) > caps.maxDepth) {\n rejectUnsafe();\n }\n // Step 7: reject oversized entries and zip bombs.\n if (entry.uncompressedSize > caps.maxEntryBytes || compressionRatio(entry) > caps.maxCompressionRatio) {\n rejectUnsafe();\n }\n // Step 8 (vetEnd): accept — append the path and add its inflated size.\n approved.push(normalized);\n total += entry.uncompressedSize;\n }\n // Step 9: reject archives exceeding the aggregate caps.\n if (approved.length > caps.maxEntries || total > caps.maxTotalUncompressedBytes) {\n rejectUnsafe();\n }\n // Step 11: return the approved plan.\n return { paths: approved, totalUncompressedBytes: total };\n}\n\n/** Assert the envelope's name/version match the inner pack manifest; throw on mismatch. */\nexport function verifyIdentity(manifest: PackArchiveManifest, packName: string, packVersion: string): void {\n // Step 1: do envelope name/version differ from the inner pack manifest?\n if (manifest.name !== packName || manifest.version !== packVersion) {\n // Step 2: reject a tampered/mis-assembled archive.\n throw new Error('envelope identity mismatch (envelope name/version differ from the inner pack manifest)');\n }\n // Step 3: identity verified.\n}\n\n/** Verify every entry's sha256 against entryDigests; false when none carried, throws on mismatch. */\nexport function verifyIntegrity(manifest: PackArchiveManifest, files: PackFile[]): boolean {\n // Step 1: did the envelope carry no integrity data?\n if (!manifest.entryDigests) {\n // Step 2: no integrity data present — return false.\n return false;\n }\n const digests = manifest.entryDigests;\n // Step 3: check each file's digest.\n for (const file of files) {\n // The envelope carries these digests and cannot digest itself — skip it.\n if (file.path === ENVELOPE_FILENAME) continue;\n // Step 4: does the recomputed sha256 differ from the recorded digest?\n if (sha256(file.contents) !== digests[file.path]) {\n // Step 7: a digest mismatch means tampering.\n throw new Error('integrity digest mismatch');\n }\n // Step 5 (checkEnd): this file's digest matches.\n }\n // Step 6: all digests matched — return true.\n return true;\n}\n\n/** Produce-side partner of verifyIntegrity: fill entryDigests + digest + stamps. */\nexport function sealIntegrity(\n manifest: PackArchiveManifest,\n files: PackFile[],\n generatedBy: string,\n generatedAt: string,\n): PackArchiveManifest {\n // Step 1: initialize an empty entryDigests map.\n const entryDigests: Record<string, string> = {};\n // Step 2: hash each file.\n for (const file of files) {\n // The envelope is the carrier of these digests — never hash it into itself.\n if (file.path === ENVELOPE_FILENAME) continue;\n // Step 3 (hashEnd): compute sha256(file.contents) and record it under file.path.\n entryDigests[file.path] = sha256(file.contents);\n }\n // Step 4: compute an overall manifest digest over the sorted entryDigests.\n const digest = sha256(canonicalDigestBytes(entryDigests));\n // Step 5: set entryDigests, digest, generatedBy, generatedAt.\n // Step 6: return the sealed manifest.\n return { ...manifest, entryDigests, digest, generatedBy, generatedAt };\n}\n\n/** Whether the archive's formatVersion + minWaironVersion are compatible with `waironVersion`. */\nexport function checkCompatibility(manifest: PackArchiveManifest, waironVersion: string): boolean {\n // Step 1: is the archive format unsupported?\n if (Math.trunc(manifest.formatVersion) > SUPPORTED_FORMAT_VERSION) {\n // Step 4: incompatible.\n return false;\n }\n // Step 2: is the running wairon below the pack's minimum?\n if (manifest.minWaironVersion && isBelow(waironVersion, manifest.minWaironVersion)) {\n // Step 4: incompatible.\n return false;\n }\n // Step 3: compatible.\n return true;\n}\n\n// --- helpers ---------------------------------------------------------------\n\nfunction rejectUnsafe(): never {\n throw new Error('reject: unsafe or oversized archive entry (zip-slip / size / ratio / depth guard)');\n}\n\nfunction validateEnvelope(raw: unknown): PackArchiveManifest {\n if (!raw || typeof raw !== 'object') {\n throw new Error('malformed pack envelope (not a YAML mapping)');\n }\n const o = raw as Record<string, unknown>;\n if (typeof o.formatVersion !== 'number') throw new Error('pack envelope: formatVersion must be a number');\n if (typeof o.name !== 'string' || !o.name) throw new Error('pack envelope: name is required');\n if (typeof o.version !== 'string' || !o.version) throw new Error('pack envelope: version is required');\n if (typeof o.kind !== 'string' || (o.kind !== 'declarative' && o.kind !== 'code')) {\n throw new Error('pack envelope: kind must be \"declarative\" or \"code\"');\n }\n if (typeof o.entry !== 'string' || !o.entry) throw new Error('pack envelope: entry is required');\n const manifest: PackArchiveManifest = {\n formatVersion: o.formatVersion,\n name: o.name,\n version: o.version,\n kind: o.kind,\n entry: o.entry,\n };\n if (typeof o.minWaironVersion === 'string') manifest.minWaironVersion = o.minWaironVersion;\n if (typeof o.digest === 'string') manifest.digest = o.digest;\n if (o.entryDigests && typeof o.entryDigests === 'object') {\n manifest.entryDigests = o.entryDigests as Record<string, string>;\n }\n if (typeof o.generatedBy === 'string') manifest.generatedBy = o.generatedBy;\n if (typeof o.generatedAt === 'string') manifest.generatedAt = o.generatedAt;\n return manifest;\n}\n\nfunction isSymlinkOrNonRegular(kind: string): boolean {\n return kind !== 'file' && kind !== 'dir';\n}\n\nfunction isStructurallyUnsafePath(p: string): boolean {\n if (p.includes('\\\\')) return true; // backslash\n if (/^[A-Za-z]:/.test(p)) return true; // drive letter\n if (p.startsWith('/')) return true; // absolute POSIX\n return p.split('/').some((seg) => seg === '..'); // parent traversal\n}\n\nfunction normalizeRelPath(p: string): string {\n return p\n .split('/')\n .filter((seg) => seg !== '' && seg !== '.')\n .join('/');\n}\n\nfunction escapesDestination(normalized: string): boolean {\n return normalized.startsWith('/') || normalized.split('/').some((seg) => seg === '..');\n}\n\nfunction pathDepth(normalized: string): number {\n return normalized.split('/').filter((seg) => seg !== '').length;\n}\n\nfunction compressionRatio(entry: ArchiveEntryMeta): number {\n if (entry.compressedSize > 0) return entry.uncompressedSize / entry.compressedSize;\n return entry.uncompressedSize > 0 ? Infinity : 0;\n}\n\nfunction canonicalDigestBytes(entryDigests: Record<string, string>): Uint8Array {\n const lines = Object.keys(entryDigests)\n .sort()\n .map((key) => `${key}:${entryDigests[key]}`)\n .join('\\n');\n return new TextEncoder().encode(lines);\n}\n\nfunction sha256(data: Uint8Array): string {\n return createHash('sha256').update(data).digest('hex');\n}\n\nfunction isBelow(running: string, minimum: string): boolean {\n const [rMaj, rMin, rPat] = parseSemverCore(running);\n const [mMaj, mMin, mPat] = parseSemverCore(minimum);\n if (rMaj !== mMaj) return rMaj < mMaj;\n if (rMin !== mMin) return rMin < mMin;\n return rPat < mPat;\n}\n\nfunction parseSemverCore(v: string): [number, number, number] {\n const cleaned = v.trim().replace(/^[^0-9]*/, ''); // strip leading ^, ~, >=, v, etc.\n const core = cleaned.split(/[-+]/)[0]; // drop prerelease/build metadata\n const parts = core.split('.').map((n) => parseInt(n, 10));\n return [parts[0] || 0, parts[1] || 0, parts[2] || 0];\n}\n","// The running @wairon/sdk version — stamped into built archives (generatedBy)\n// and used as the \"running wairon version\" for pack version-compatibility\n// checks. tsup inlines package.json at build time; at runtime the require\n// resolves against the shipped package (../package.json from dist/).\n\nexport const SDK_VERSION: string = loadVersion();\n\nfunction loadVersion(): string {\n try {\n const pkg = require('../package.json') as { version?: string };\n return pkg.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n","import type { PackFile, PackScaffoldRequest } from './types.js';\nimport { SDK_VERSION } from './version.js';\n\n// ---------------------------------------------------------------------------\n// Pack Scaffold Specialist (pack_scaffold_specialist_impl) — PURE template\n// rendering for `wairon pack init`. Produces an in-memory file map; writes\n// nothing. Code packs pin @wairon/sdk to the running SDK version.\n// ---------------------------------------------------------------------------\n\nconst enc = (s: string): Uint8Array => new TextEncoder().encode(s);\n\n/** Render the scaffold file map for the requested pack (declarative or code). */\nexport function render(request: PackScaffoldRequest): PackFile[] {\n const version = request.version ?? '0.1.0';\n let files: PackFile[];\n // Step 1: which variant?\n if (request.kind === 'code') {\n // Step 2: render the code-pack file map.\n files = renderCodePack(request.name, version);\n // Step 3: skip the declarative variant (fall through to the skill gate).\n } else {\n // Step 4 (declVariant): render the declarative-pack file map.\n files = renderDeclarativePack(request.name, version);\n }\n // Step 5 (skillGate): include a skill stub?\n if (request.withSkill) {\n // Step 6: append a skills/<id>/SKILL.md stub to the file map.\n files.push(renderSkillStub(request.name, version));\n }\n // Step 7 (done): return the rendered file map.\n return files;\n}\n\n// --- variant renderers -----------------------------------------------------\n\nfunction renderDeclarativePack(name: string, version: string): PackFile[] {\n return [\n { path: 'wairon-pack.yaml', contents: enc(declarativeEnvelope(name, version)) },\n { path: 'pack.yaml', contents: enc(declarativeManifest(name, version)) },\n { path: 'README.md', contents: enc(readme(name, 'declarative', 'pack.yaml')) },\n ];\n}\n\nfunction renderCodePack(name: string, version: string): PackFile[] {\n return [\n { path: 'wairon-pack.yaml', contents: enc(codeEnvelope(name, version)) },\n { path: 'package.json', contents: enc(codePackageJson(name, version)) },\n { path: 'tsconfig.json', contents: enc(codeTsconfig()) },\n { path: 'pack.ts', contents: enc(codeEntry(name, version)) },\n { path: 'README.md', contents: enc(readme(name, 'code', 'pack.cjs')) },\n ];\n}\n\nfunction renderSkillStub(name: string, _version: string): PackFile {\n const id = slug(name);\n return { path: `skills/${id}/SKILL.md`, contents: enc(skillStub(name)) };\n}\n\n// --- templates -------------------------------------------------------------\n\nfunction declarativeEnvelope(name: string, version: string): string {\n return [\n 'formatVersion: 1',\n `name: ${name}`,\n `version: ${version}`,\n 'kind: declarative',\n 'entry: pack.yaml',\n '',\n ].join('\\n');\n}\n\nfunction codeEnvelope(name: string, version: string): string {\n return [\n 'formatVersion: 1',\n `name: ${name}`,\n `version: ${version}`,\n 'kind: code',\n 'entry: pack.cjs',\n `minWaironVersion: ${SDK_VERSION}`,\n '',\n ].join('\\n');\n}\n\nfunction declarativeManifest(name: string, version: string): string {\n return [\n `name: ${name}`,\n `version: ${version}`,\n '',\n '# Custom architectural profiles this pack contributes (example, commented):',\n '# profiles:',\n '# my-profile:',\n '# family: backend-like',\n '# forbiddenStereotypes:',\n '# - types: [Adapter]',\n '# reason: \"Domain components must not touch adapters directly.\"',\n 'profiles: {}',\n '',\n '# Target language / platform tables (example, commented):',\n '# languages:',\n '# rust:',\n '# unsupportedFlow: {}',\n '# foreignBuiltins: []',\n 'languages: {}',\n '',\n '# Reusable, versioned architecture patterns:',\n 'patterns: []',\n '',\n ].join('\\n');\n}\n\nfunction codePackageJson(name: string, version: string): string {\n const pkg = {\n name,\n version,\n private: true,\n scripts: {\n build: 'esbuild pack.ts --bundle --platform=node --packages=external --format=cjs --outfile=pack.cjs',\n },\n dependencies: {\n '@wairon/sdk': SDK_VERSION,\n },\n devDependencies: {\n esbuild: '^0.21.0',\n typescript: '^5.0.0',\n },\n };\n return `${JSON.stringify(pkg, null, 2)}\\n`;\n}\n\nfunction codeTsconfig(): string {\n const tsconfig = {\n compilerOptions: {\n target: 'ES2020',\n module: 'CommonJS',\n moduleResolution: 'node',\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n declaration: false,\n noEmit: true,\n },\n include: ['pack.ts'],\n };\n return `${JSON.stringify(tsconfig, null, 2)}\\n`;\n}\n\nfunction codeEntry(name: string, version: string): string {\n const code = slug(name).toUpperCase().replace(/-/g, '_');\n return [\n \"import { defineRule } from '@wairon/sdk';\",\n \"import type { RuleContext, Finding } from '@wairon/sdk';\",\n '',\n 'const exampleRule = defineRule({',\n ` name: '${slug(name)}-example',`,\n \" description: 'Example architectural rule. Replace with your own doctrine.',\",\n ` codes: ['${code}_EXAMPLE'],`,\n ' check(ctx: RuleContext): Finding[] {',\n ' const findings: Finding[] = [];',\n ' for (const component of ctx.components) {',\n ' // Inspect the spec tree and push findings as needed.',\n ' void component;',\n ' }',\n ' return findings;',\n ' },',\n '});',\n '',\n 'export default {',\n ` name: '${name}',`,\n ` version: '${version}',`,\n ' rules: [exampleRule],',\n '};',\n '',\n ].join('\\n');\n}\n\nfunction readme(name: string, kind: string, entry: string): string {\n return [\n `# ${name}`,\n '',\n `A wairon ${kind} pack.`,\n '',\n `- Envelope: \\`wairon-pack.yaml\\``,\n `- Entry: \\`${entry}\\``,\n '',\n 'Build an installable `.wpack` archive with `wairon pack build`.',\n '',\n ].join('\\n');\n}\n\nfunction skillStub(name: string): string {\n return [\n '---',\n `name: ${name}`,\n `description: ${name} authoring skill (stub).`,\n '---',\n '',\n `# ${name}`,\n '',\n 'Describe when this skill applies and what it guides.',\n '',\n ].join('\\n');\n}\n\nfunction slug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport { zipSync, unzipSync, type Zippable } from 'fflate';\nimport type { ArchiveEntryMeta, PackFile } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Pack Archive Adapter (pack_archive_adapter_impl) — the ONLY zip-library + fs\n// I/O boundary of the SDK. Technology: fflate (pure-JS ZIP, no native deps).\n//\n// Acts only on already-validated inputs: it makes no path-safety or format\n// decisions (that is the codec's job). Enumeration reads sizes WITHOUT\n// inflating so the codec can enforce zip-bomb / size caps before any bytes are\n// decompressed.\n// ---------------------------------------------------------------------------\n\nconst SKIP_DIRS = new Set(['node_modules', '.git', '.hg', '.svn']);\n\n/**\n * Enumerate every entry's metadata (path, compressed + uncompressed size, kind)\n * WITHOUT decompressing any bytes, so the codec can enforce caps before\n * inflation.\n */\nexport function listEntries(archive: Uint8Array): ArchiveEntryMeta[] {\n // Step 1: read the central directory via the zip library, mapping each record\n // to ArchiveEntryMeta without inflating any data. fflate's filter reports\n // {name, size (compressed), originalSize (uncompressed)} for every entry;\n // returning false skips decompression. Symlink detection reads the central\n // directory's external attributes (fflate's metadata omits the unix mode).\n const symlinks = detectSymlinkNames(archive);\n const metas: ArchiveEntryMeta[] = [];\n unzipSync(archive, {\n filter: (info): boolean => {\n metas.push({\n path: info.name,\n uncompressedSize: info.originalSize,\n compressedSize: info.size,\n kind: classifyKind(info.name, symlinks),\n });\n return false;\n },\n });\n // Step 2: return the entry metadata list.\n return metas;\n}\n\n/** Decompress and return the bytes of a single named entry. */\nexport function inflateEntry(archive: Uint8Array, entryPath: string): Uint8Array {\n // Step 1: locate the named entry and inflate only it.\n const inflated = unzipSync(archive, { filter: (info): boolean => info.name === entryPath });\n const bytes = inflated[entryPath];\n if (!bytes) throw new Error(`archive entry not found: ${entryPath}`);\n // Step 2: return the entry's decompressed bytes.\n return bytes;\n}\n\n/** Deflate a set of files (the envelope + pack contents) into a single ZIP buffer. */\nexport function assembleArchive(files: PackFile[]): Uint8Array {\n // Step 1: add each file to a zip container (deflating) and finalize.\n const zippable: Zippable = {};\n for (const file of files) zippable[file.path] = file.contents;\n const bytes = zipSync(zippable);\n // Step 2: return the archive bytes.\n return bytes;\n}\n\n/** Read a source pack directory recursively into a file map (POSIX paths + bytes). */\nexport function readPackDir(dir: string): PackFile[] {\n // Step 1: walk the directory recursively, reading each file into\n // { path: POSIX-relative, contents } (skipping node_modules and VCS dirs).\n const files: PackFile[] = [];\n walkPackDir(dir, dir, files);\n // Step 2: return the file map.\n return files;\n}\n\n/** Write a file map as a directory tree under destDir, creating parent directories. */\nexport function writeTree(destDir: string, files: PackFile[]): void {\n // Step 1: write each approved file.\n for (const file of files) {\n // Step 2 (writeEnd): create parent directories and write the file's bytes.\n const absolute = path.join(destDir, file.path);\n fs.mkdirSync(path.dirname(absolute), { recursive: true });\n fs.writeFileSync(absolute, file.contents);\n }\n // Step 3: done.\n}\n\n// --- helpers ---------------------------------------------------------------\n\nfunction classifyKind(name: string, symlinks: Set<string>): string {\n if (symlinks.has(name)) return 'symlink';\n if (name.endsWith('/')) return 'dir';\n return 'file';\n}\n\nfunction walkPackDir(root: string, current: string, out: PackFile[]): void {\n for (const entry of fs.readdirSync(current, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n walkPackDir(root, path.join(current, entry.name), out);\n } else if (entry.isFile()) {\n const absolute = path.join(current, entry.name);\n const relative = path.relative(root, absolute).split(path.sep).join('/');\n out.push({ path: relative, contents: fs.readFileSync(absolute) });\n }\n }\n}\n\n// Best-effort central-directory scan for symlink entries (unix S_IFLNK in the\n// external file attributes). fflate's high-level metadata omits the unix mode,\n// so the codec's symlink rejection needs this. Any parse trouble yields an\n// empty set — no worse than fflate alone, and confinement/size caps still apply.\nfunction detectSymlinkNames(archive: Uint8Array): Set<string> {\n const names = new Set<string>();\n try {\n const eocd = findEocd(archive);\n if (eocd < 0) return names;\n let p = readU32(archive, eocd + 16); // central directory offset\n const count = readU16(archive, eocd + 10);\n const decoder = new TextDecoder();\n for (let i = 0; i < count; i++) {\n if (readU32(archive, p) !== 0x02014b50) break; // central file header sig\n const versionMadeBy = readU16(archive, p + 4);\n const nameLen = readU16(archive, p + 28);\n const extraLen = readU16(archive, p + 30);\n const commentLen = readU16(archive, p + 32);\n const externalAttrs = readU32(archive, p + 38);\n const name = decoder.decode(archive.subarray(p + 46, p + 46 + nameLen));\n if ((versionMadeBy >> 8) === 3) { // unix host\n const unixMode = (externalAttrs >>> 16) & 0xffff;\n if ((unixMode & 0o170000) === 0o120000) names.add(name);\n }\n p += 46 + nameLen + extraLen + commentLen;\n }\n } catch {\n return names;\n }\n return names;\n}\n\nfunction findEocd(buf: Uint8Array): number {\n const minRecord = 22;\n const lowerBound = Math.max(0, buf.length - (minRecord + 0xffff));\n for (let i = buf.length - minRecord; i >= lowerBound; i--) {\n if (readU32(buf, i) === 0x06054b50) return i;\n }\n return -1;\n}\n\nfunction readU16(buf: Uint8Array, off: number): number {\n return buf[off] | (buf[off + 1] << 8);\n}\n\nfunction readU32(buf: Uint8Array, off: number): number {\n return (buf[off] | (buf[off + 1] << 8) | (buf[off + 2] << 16) | (buf[off + 3] << 24)) >>> 0;\n}\n","// ---------------------------------------------------------------------------\n// Rule-authoring contract (the code-pack compile target).\n//\n// Code packs are written against `@wairon/sdk`, never against wairon core. This\n// is a small, stable, hand-authored facade — deliberately NOT core's fat\n// RuleContext. `defineRule` returns the rule unchanged (identity + type\n// inference); the runtime shape `{ name, description?, codes: string[], check }`\n// is exactly what the core extension loader duck-types.\n// ---------------------------------------------------------------------------\n\n/** Severity of a finding a pack rule emits. */\nexport type RuleSeverity = 'error' | 'warning';\n\n/** A single issue a pack rule reports about the spec tree. */\nexport interface Finding {\n /** Pack-local issue code (surfaced namespaced by wairon, e.g. `<PACK>_<CODE>`). */\n code: string;\n /** 'error' | 'warning'. */\n severity: RuleSeverity;\n /** Human-readable explanation of the violation. */\n message: string;\n /** The spec id the finding is anchored to (optional). */\n specId?: string;\n}\n\n/**\n * A read-only spec node as seen by a pack rule: its id/name plus arbitrary\n * further fields (kept open so the facade stays stable as the spec model grows).\n */\nexport interface RuleSpecNode {\n id: string;\n name?: string;\n [key: string]: unknown;\n}\n\n/**\n * The minimal, stable view of the spec tree a code-pack rule inspects. A rule\n * reads these collections and returns findings — it never mutates state and\n * never reaches into wairon core internals.\n */\nexport interface RuleContext {\n /** The L0 system spec. */\n system: RuleSpecNode;\n /** All L1 subsystem specs. */\n subsystems: RuleSpecNode[];\n /** All L2 component specs. */\n components: RuleSpecNode[];\n /** All L3 interface specs. */\n interfaces: RuleSpecNode[];\n /** All L4 implementation specs. */\n implementations: RuleSpecNode[];\n /** All shared type/value-object specs. */\n types: RuleSpecNode[];\n}\n\n/**\n * A programmatic conformance rule shipped by a code pack. Its runtime shape —\n * `{ name, description?, codes: string[], check }` — is what wairon's extension\n * loader duck-types when it loads a `pack.cjs`.\n */\nexport interface SddRule {\n /** Stable rule id (kebab-case), e.g. \"my-portal-transport\". */\n name: string;\n /** One-line description of what the rule enforces and why. */\n description?: string;\n /** The issue codes this rule can emit. */\n codes: string[];\n /** Inspect the spec tree and return findings (empty when clean). */\n check(ctx: RuleContext): Finding[];\n}\n\n/**\n * Author an SddRule with full type inference. Returns the rule unchanged — it\n * exists purely so code packs get typed authoring without importing anything\n * from wairon core.\n */\nexport function defineRule(rule: SddRule): SddRule {\n return rule;\n}\n"],"mappings":"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","names":["exports","module","buildPack","extractPack","inspectArchive","scaffoldPack","path","import_js_yaml","yamlLoad","yamlDump","relative","yamlLoad","scaffoldPack","buildPack","inspectArchive","extractPack"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wairon/sdk",
|
|
3
|
+
"version": "5.0.1-dev.5",
|
|
4
|
+
"description": "Wairon pack-archive (.wpack) format authority and authoring toolkit — scaffold, build, inspect, and safely extract wairon extension packs.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"wairon",
|
|
7
|
+
"sdd",
|
|
8
|
+
"pack",
|
|
9
|
+
"wpack",
|
|
10
|
+
"archive",
|
|
11
|
+
"developer-tools"
|
|
12
|
+
],
|
|
13
|
+
"author": "SYW",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://github.com/SYW-Apps/Waffle-AIron",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/SYW-Apps/Waffle-AIron.git"
|
|
19
|
+
},
|
|
20
|
+
"type": "commonjs",
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"test:watch": "vitest",
|
|
35
|
+
"clean": "rimraf dist"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"fflate": "^0.8.2",
|
|
39
|
+
"js-yaml": "^4.1.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/js-yaml": "^4.0.9",
|
|
43
|
+
"@types/node": "^20.14.0",
|
|
44
|
+
"rimraf": "^5.0.7",
|
|
45
|
+
"tsup": "^8.1.0",
|
|
46
|
+
"typescript": "^5.5.2",
|
|
47
|
+
"vitest": "^4.1.9"
|
|
48
|
+
}
|
|
49
|
+
}
|