@synopackageland/cli 0.1.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.
Files changed (170) hide show
  1. package/README.md +13 -0
  2. package/dist/attestation/app-package.d.ts +86 -0
  3. package/dist/attestation/app-package.js +199 -0
  4. package/dist/attestation/install-on-host.d.ts +14 -0
  5. package/dist/attestation/install-on-host.js +52 -0
  6. package/dist/attestation/keyring.d.ts +21 -0
  7. package/dist/attestation/keyring.js +168 -0
  8. package/dist/bin.d.ts +2 -0
  9. package/dist/bin.js +14 -0
  10. package/dist/build-spk/dsm-arch.d.ts +4 -0
  11. package/dist/build-spk/dsm-arch.js +32 -0
  12. package/dist/build-spk/dsm-icons.d.ts +2 -0
  13. package/dist/build-spk/dsm-icons.js +67 -0
  14. package/dist/build-spk/generator.d.ts +43 -0
  15. package/dist/build-spk/generator.js +653 -0
  16. package/dist/build-spk/native-payload.d.ts +22 -0
  17. package/dist/build-spk/native-payload.js +654 -0
  18. package/dist/build-spk/reproducible-archive.d.ts +8 -0
  19. package/dist/build-spk/reproducible-archive.js +34 -0
  20. package/dist/cli.d.ts +1 -0
  21. package/dist/cli.js +345 -0
  22. package/dist/compose/analyze.d.ts +25 -0
  23. package/dist/compose/analyze.js +346 -0
  24. package/dist/compose/env.d.ts +7 -0
  25. package/dist/compose/env.js +36 -0
  26. package/dist/compose/privilege.d.ts +14 -0
  27. package/dist/compose/privilege.js +102 -0
  28. package/dist/compose/runner.d.ts +32 -0
  29. package/dist/compose/runner.js +84 -0
  30. package/dist/contract/load.d.ts +25 -0
  31. package/dist/contract/load.js +87 -0
  32. package/dist/contract/schema-validate.d.ts +3 -0
  33. package/dist/contract/schema-validate.js +50 -0
  34. package/dist/contract/validate.d.ts +3 -0
  35. package/dist/contract/validate.js +80 -0
  36. package/dist/contract/web-launch.d.ts +19 -0
  37. package/dist/contract/web-launch.js +94 -0
  38. package/dist/deploy/catalog-publish.d.ts +3 -0
  39. package/dist/deploy/catalog-publish.js +7 -0
  40. package/dist/deploy/deploy.d.ts +22 -0
  41. package/dist/deploy/deploy.js +136 -0
  42. package/dist/deploy/identity-shared.d.ts +3 -0
  43. package/dist/deploy/identity-shared.js +14 -0
  44. package/dist/deploy/identity.d.ts +7 -0
  45. package/dist/deploy/identity.js +20 -0
  46. package/dist/deploy/package-source.d.ts +7 -0
  47. package/dist/deploy/package-source.js +17 -0
  48. package/dist/deploy/run-as-values.d.ts +18 -0
  49. package/dist/deploy/run-as-values.js +48 -0
  50. package/dist/deploy/share-values.d.ts +20 -0
  51. package/dist/deploy/share-values.js +60 -0
  52. package/dist/deploy/spk-info.d.ts +12 -0
  53. package/dist/deploy/spk-info.js +41 -0
  54. package/dist/deploy/spk-version.d.ts +4 -0
  55. package/dist/deploy/spk-version.js +64 -0
  56. package/dist/dev.d.ts +13 -0
  57. package/dist/dev.js +31 -0
  58. package/dist/discovery.d.ts +8 -0
  59. package/dist/discovery.js +22 -0
  60. package/dist/errors.d.ts +6 -0
  61. package/dist/errors.js +18 -0
  62. package/dist/host/broker-paths.d.ts +1 -0
  63. package/dist/host/broker-paths.js +1 -0
  64. package/dist/host/catalog-install.d.ts +59 -0
  65. package/dist/host/catalog-install.js +326 -0
  66. package/dist/host/credentials.d.ts +2 -0
  67. package/dist/host/credentials.js +37 -0
  68. package/dist/host/digests.d.ts +5 -0
  69. package/dist/host/digests.js +55 -0
  70. package/dist/host/dsm-client.d.ts +6 -0
  71. package/dist/host/dsm-client.js +290 -0
  72. package/dist/host/dsm-http.d.ts +1 -0
  73. package/dist/host/dsm-http.js +87 -0
  74. package/dist/host/fake-nas.d.ts +16 -0
  75. package/dist/host/fake-nas.js +116 -0
  76. package/dist/host/open-url.d.ts +5 -0
  77. package/dist/host/open-url.js +13 -0
  78. package/dist/host/share-lookup.d.ts +3 -0
  79. package/dist/host/share-lookup.js +144 -0
  80. package/dist/host/types.d.ts +67 -0
  81. package/dist/host/types.js +1 -0
  82. package/dist/index.d.ts +19 -0
  83. package/dist/index.js +14 -0
  84. package/dist/info.d.ts +17 -0
  85. package/dist/info.js +34 -0
  86. package/dist/init.d.ts +4 -0
  87. package/dist/init.js +31 -0
  88. package/dist/inspect.d.ts +13 -0
  89. package/dist/inspect.js +102 -0
  90. package/dist/load-env.d.ts +1 -0
  91. package/dist/load-env.js +31 -0
  92. package/dist/log.d.ts +13 -0
  93. package/dist/log.js +20 -0
  94. package/dist/mock-broker/bootstrap.d.ts +2 -0
  95. package/dist/mock-broker/bootstrap.js +8 -0
  96. package/dist/mock-broker/files.d.ts +60 -0
  97. package/dist/mock-broker/files.js +408 -0
  98. package/dist/mock-broker/oidc.d.ts +31 -0
  99. package/dist/mock-broker/oidc.js +161 -0
  100. package/dist/mock-broker/path.d.ts +9 -0
  101. package/dist/mock-broker/path.js +50 -0
  102. package/dist/mock-broker/server.d.ts +19 -0
  103. package/dist/mock-broker/server.js +350 -0
  104. package/dist/publish/build-ledger.d.ts +22 -0
  105. package/dist/publish/build-ledger.js +26 -0
  106. package/dist/publish/community-path.d.ts +6 -0
  107. package/dist/publish/community-path.js +41 -0
  108. package/dist/publish/curated-release.d.ts +78 -0
  109. package/dist/publish/curated-release.js +693 -0
  110. package/dist/publish/file-build-ledger.d.ts +18 -0
  111. package/dist/publish/file-build-ledger.js +268 -0
  112. package/dist/publish/github-pr.d.ts +25 -0
  113. package/dist/publish/github-pr.js +85 -0
  114. package/dist/publish/github-repo.d.ts +8 -0
  115. package/dist/publish/github-repo.js +21 -0
  116. package/dist/publish/publish.d.ts +23 -0
  117. package/dist/publish/publish.js +128 -0
  118. package/dist/publish/sync-definition.d.ts +4 -0
  119. package/dist/publish/sync-definition.js +48 -0
  120. package/dist/skill.d.ts +10 -0
  121. package/dist/skill.js +50 -0
  122. package/dist/templates/compose-import/compose.yaml +11 -0
  123. package/dist/templates/compose-import/synology-app.yaml +15 -0
  124. package/dist/templates/hello-files/com.synology.hello.files.dev.spk +0 -0
  125. package/dist/templates/hello-files/compose.yaml +8 -0
  126. package/dist/templates/hello-files/synology-app.yaml +13 -0
  127. package/dist/templates/hello-web/compose.yaml +7 -0
  128. package/dist/templates/hello-web/html/app.js +23 -0
  129. package/dist/templates/hello-web/html/index.html +15 -0
  130. package/dist/templates/hello-web/html/sdk.js +68 -0
  131. package/dist/templates/hello-web/synology-app.yaml +13 -0
  132. package/dist/templates/hello-web-page/com.synology.hello.web.page.dev.spk +0 -0
  133. package/dist/templates/hello-web-page/compose.yaml +7 -0
  134. package/dist/templates/hello-web-page/html/app.js +23 -0
  135. package/dist/templates/hello-web-page/html/index.html +15 -0
  136. package/dist/templates/hello-web-page/html/sdk.js +68 -0
  137. package/dist/templates/hello-web-page/synology-app.yaml +13 -0
  138. package/dist/test-command.d.ts +13 -0
  139. package/dist/test-command.js +26 -0
  140. package/dist/test-host.d.ts +13 -0
  141. package/dist/test-host.js +24 -0
  142. package/dist/types.d.ts +59 -0
  143. package/dist/types.js +1 -0
  144. package/dist/validate.d.ts +8 -0
  145. package/dist/validate.js +103 -0
  146. package/package.json +38 -0
  147. package/schema/synology-app.schema.json +137 -0
  148. package/skills/SKILL.md +51 -0
  149. package/skills/reference/create.md +34 -0
  150. package/skills/reference/deploy.md +41 -0
  151. package/skills/reference/diagnose.md +42 -0
  152. package/skills/reference/files.md +37 -0
  153. package/skills/reference/identity.md +31 -0
  154. package/skills/reference/wrap.md +35 -0
  155. package/templates/compose-import/compose.yaml +11 -0
  156. package/templates/compose-import/synology-app.yaml +15 -0
  157. package/templates/hello-files/com.synology.hello.files.dev.spk +0 -0
  158. package/templates/hello-files/compose.yaml +8 -0
  159. package/templates/hello-files/synology-app.yaml +13 -0
  160. package/templates/hello-web/compose.yaml +7 -0
  161. package/templates/hello-web/html/app.js +23 -0
  162. package/templates/hello-web/html/index.html +15 -0
  163. package/templates/hello-web/html/sdk.js +68 -0
  164. package/templates/hello-web/synology-app.yaml +13 -0
  165. package/templates/hello-web-page/com.synology.hello.web.page.dev.spk +0 -0
  166. package/templates/hello-web-page/compose.yaml +7 -0
  167. package/templates/hello-web-page/html/app.js +23 -0
  168. package/templates/hello-web-page/html/index.html +15 -0
  169. package/templates/hello-web-page/html/sdk.js +68 -0
  170. package/templates/hello-web-page/synology-app.yaml +13 -0
@@ -0,0 +1,67 @@
1
+ export type AttestationKind = "local" | "app-package" | "none";
2
+ export interface DeployIdentity {
3
+ packageId: string;
4
+ displayname: string;
5
+ developmentInstance: boolean;
6
+ }
7
+ export interface AttestationIssueResult {
8
+ kind: "local";
9
+ attestationDigest: string;
10
+ }
11
+ export interface HostAttestationInfo {
12
+ packageIdentity: string;
13
+ attestationKind: AttestationKind;
14
+ developmentInstance: boolean;
15
+ openUrl?: string;
16
+ readiness?: string;
17
+ projectName?: string;
18
+ }
19
+ export interface HostInstallRecord {
20
+ packageId: string;
21
+ version?: string;
22
+ source?: "package-center" | "local-spk";
23
+ extraValues?: Record<string, string>;
24
+ }
25
+ export interface NasHost {
26
+ readonly dsmOrigin: string;
27
+ readonly installationId: string;
28
+ requestAttestation(input: {
29
+ packageIdentity: string;
30
+ contractDigest: string;
31
+ bundleDigest: string;
32
+ capabilities: string[];
33
+ allowCuratedDowngrade?: boolean;
34
+ }): Promise<AttestationIssueResult>;
35
+ installFromCatalog(input: {
36
+ packageId: string;
37
+ version?: string;
38
+ extraValues?: Record<string, string>;
39
+ }): Promise<void>;
40
+ installFromLocalSpk?(input: {
41
+ packageId: string;
42
+ version: string;
43
+ spkPath: string;
44
+ extraValues?: Record<string, string>;
45
+ }): Promise<void>;
46
+ resolveSharePath?(logicalName: string): Promise<string | undefined>;
47
+ getInstalledProjectEnv?(packageIdentity: string): Promise<Record<string, string> | undefined>;
48
+ ensurePackageSource?(origin: string): Promise<void>;
49
+ getInstalledPackageVersion?(packageIdentity: string): Promise<string | undefined>;
50
+ getAttestationInfo(packageIdentity: string): Promise<HostAttestationInfo>;
51
+ isPackageInstalled(packageIdentity: string): Promise<boolean>;
52
+ fetchContainerLogs(packageIdentity: string): Promise<string[]>;
53
+ smokeRequireLogin(input: {
54
+ packageIdentity: string;
55
+ cookieHeader: string;
56
+ }): Promise<{
57
+ ok: boolean;
58
+ username?: string;
59
+ }>;
60
+ markCurated?(packageIdentity: string): void;
61
+ }
62
+ export interface HostCredentials {
63
+ dsmOrigin: string;
64
+ username: string;
65
+ password: string;
66
+ cookieHeader?: string;
67
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ export { runCli } from "./cli.js";
2
+ export { validateProject, findProjectRoot } from "./validate.js";
3
+ export type { AnalyzerProfile } from "./validate.js";
4
+ export { inspectProject, formatInspectReport } from "./inspect.js";
5
+ export { initFromTemplate, isTemplateName } from "./init.js";
6
+ export { runDev } from "./dev.js";
7
+ export { testProject } from "./test-command.js";
8
+ export { buildSpk } from "./build-spk/generator.js";
9
+ export { publishProject, resolveCommunityAppsRoot } from "./publish/publish.js";
10
+ export { communityAppsGitHubRepo, communityAppsGitHubUrl, DEFAULT_GITHUB_OWNER, } from "./publish/github-repo.js";
11
+ export { InMemoryBuildLedger } from "./publish/build-ledger.js";
12
+ export type { BuildLedger } from "./publish/build-ledger.js";
13
+ export { createCuratedAttestationSignerFromEnv, isPublicOciDigest, releaseCurated, runCuratedRelease, runCuratedReleaseCli, digestReleaseBundle, validateCuratedCompose, } from "./publish/curated-release.js";
14
+ export type { CuratedAttestationSignInput, CuratedAttestationSigner, CuratedComposeGateResult, CuratedReleaseSignInput, CuratedReleaseOptions, CuratedReleaseResult, } from "./publish/curated-release.js";
15
+ export { canonicalAppPackageClaims, canonicalCuratedReleaseClaims, digestAppPackageClaims, digestCuratedReleaseClaims, normalizeCapabilities, assertPackageVersionBinding, signCuratedAppPackageAttestationToken, signCuratedReleaseAttestationToken, } from "./attestation/app-package.js";
16
+ export type { AppPackageAttestationClaims, AppPackageSigningKey, CuratedAppPackageAttestationInput, CuratedReleaseAttestationClaims, CuratedReleaseAttestationInput, } from "./attestation/app-package.js";
17
+ export { startMockBroker, FAKE_USER } from "./mock-broker/server.js";
18
+ export { RecordingComposeRunner } from "./compose/runner.js";
19
+ export type { ValidationError, ValidationResult } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ export { runCli } from "./cli.js";
2
+ export { validateProject, findProjectRoot } from "./validate.js";
3
+ export { inspectProject, formatInspectReport } from "./inspect.js";
4
+ export { initFromTemplate, isTemplateName } from "./init.js";
5
+ export { runDev } from "./dev.js";
6
+ export { testProject } from "./test-command.js";
7
+ export { buildSpk } from "./build-spk/generator.js";
8
+ export { publishProject, resolveCommunityAppsRoot } from "./publish/publish.js";
9
+ export { communityAppsGitHubRepo, communityAppsGitHubUrl, DEFAULT_GITHUB_OWNER, } from "./publish/github-repo.js";
10
+ export { InMemoryBuildLedger } from "./publish/build-ledger.js";
11
+ export { createCuratedAttestationSignerFromEnv, isPublicOciDigest, releaseCurated, runCuratedRelease, runCuratedReleaseCli, digestReleaseBundle, validateCuratedCompose, } from "./publish/curated-release.js";
12
+ export { canonicalAppPackageClaims, canonicalCuratedReleaseClaims, digestAppPackageClaims, digestCuratedReleaseClaims, normalizeCapabilities, assertPackageVersionBinding, signCuratedAppPackageAttestationToken, signCuratedReleaseAttestationToken, } from "./attestation/app-package.js";
13
+ export { startMockBroker, FAKE_USER } from "./mock-broker/server.js";
14
+ export { RecordingComposeRunner } from "./compose/runner.js";
package/dist/info.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { ProjectRoot } from "./discovery.js";
2
+ import type { NasHost } from "./host/types.js";
3
+ export interface ProjectInfoOptions {
4
+ project: ProjectRoot;
5
+ host: NasHost;
6
+ dev?: boolean;
7
+ }
8
+ export declare function projectInfo(options: ProjectInfoOptions): Promise<ProjectInfoResult>;
9
+ export interface ProjectInfoResult {
10
+ packageIdentity: string;
11
+ attestationKind: "local" | "app-package" | "none";
12
+ developmentInstance: boolean;
13
+ openUrl?: string;
14
+ readiness?: string;
15
+ installed: boolean;
16
+ }
17
+ export declare function formatProjectInfo(info: ProjectInfoResult): string;
package/dist/info.js ADDED
@@ -0,0 +1,34 @@
1
+ import { loadContract } from "./contract/load.js";
2
+ import { appOriginFromContract } from "./host/open-url.js";
3
+ import { resolveDeployIdentity } from "./deploy/identity.js";
4
+ export async function projectInfo(options) {
5
+ const identity = resolveDeployIdentity(options.project, { dev: options.dev });
6
+ const remote = await options.host.getAttestationInfo(identity.packageId);
7
+ const installed = await options.host.isPackageInstalled(identity.packageId);
8
+ const contract = loadContract(options.project.contractPath).contract;
9
+ return {
10
+ packageIdentity: identity.packageId,
11
+ attestationKind: remote.attestationKind,
12
+ developmentInstance: identity.developmentInstance,
13
+ openUrl: remote.openUrl ?? appOriginFromContract(options.host.dsmOrigin, contract),
14
+ readiness: remote.readiness ?? (installed ? "UNKNOWN" : "NOT_INSTALLED"),
15
+ installed,
16
+ };
17
+ }
18
+ export function formatProjectInfo(info) {
19
+ const attestationLabel = info.attestationKind === "app-package"
20
+ ? "Curated(App Package Attestation)"
21
+ : info.attestationKind === "local"
22
+ ? "Local(Local Attestation)"
23
+ : "無";
24
+ return [
25
+ `Package Identity:${info.packageIdentity}`,
26
+ `Attestation:${attestationLabel}`,
27
+ `開發用 Instance:${info.developmentInstance ? "是" : "否"}`,
28
+ `已安裝:${info.installed ? "是" : "否"}`,
29
+ info.openUrl ? `Open URL:${info.openUrl}` : "Open URL:(未設定)",
30
+ info.readiness ? `Readiness:${info.readiness}` : "",
31
+ ]
32
+ .filter(Boolean)
33
+ .join("\n");
34
+ }
package/dist/init.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { TemplateName } from "./types.js";
2
+ export declare function isTemplateName(value: string): value is TemplateName;
3
+ export declare function listTemplates(): TemplateName[];
4
+ export declare function initFromTemplate(template: TemplateName, targetDir: string): void;
package/dist/init.js ADDED
@@ -0,0 +1,31 @@
1
+ import { mkdirSync, cpSync, existsSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const TEMPLATE_NAMES = [
5
+ "hello-web",
6
+ "hello-web-page",
7
+ "hello-files",
8
+ "compose-import",
9
+ ];
10
+ export function isTemplateName(value) {
11
+ return TEMPLATE_NAMES.includes(value);
12
+ }
13
+ export function listTemplates() {
14
+ return [...TEMPLATE_NAMES];
15
+ }
16
+ function templatesRoot() {
17
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
18
+ const distTemplates = join(moduleDir, "templates");
19
+ if (existsSync(distTemplates)) {
20
+ return distTemplates;
21
+ }
22
+ return join(moduleDir, "..", "templates");
23
+ }
24
+ export function initFromTemplate(template, targetDir) {
25
+ const source = join(templatesRoot(), template);
26
+ if (!existsSync(source)) {
27
+ throw new Error(`找不到模板:${template}`);
28
+ }
29
+ mkdirSync(targetDir, { recursive: true });
30
+ cpSync(source, targetDir, { recursive: true });
31
+ }
@@ -0,0 +1,13 @@
1
+ import { type ProjectRoot } from "./discovery.js";
2
+ export interface InspectReport {
3
+ services: string[];
4
+ publishedPorts: Array<{
5
+ port: number;
6
+ services: string[];
7
+ }>;
8
+ dataMounts: string[];
9
+ risks: string[];
10
+ webHints: string[];
11
+ }
12
+ export declare function inspectProject(project: ProjectRoot): InspectReport;
13
+ export declare function formatInspectReport(report: InspectReport): string;
@@ -0,0 +1,102 @@
1
+ import { existsSync } from "node:fs";
2
+ import { collectPublishedTcpPorts, listServiceNames, loadCompose, } from "./compose/analyze.js";
3
+ export function inspectProject(project) {
4
+ const services = [];
5
+ const publishedPorts = [];
6
+ const dataMounts = [];
7
+ const risks = [];
8
+ const webHints = [];
9
+ if (!existsSync(project.composePath)) {
10
+ return {
11
+ services,
12
+ publishedPorts,
13
+ dataMounts,
14
+ risks: ["缺少 compose.yaml"],
15
+ webHints,
16
+ };
17
+ }
18
+ const parsed = loadCompose(project.composePath);
19
+ if (!parsed) {
20
+ return { services, publishedPorts, dataMounts, risks, webHints };
21
+ }
22
+ const compose = parsed.compose;
23
+ services.push(...listServiceNames(compose));
24
+ for (const [port, serviceNames] of collectPublishedTcpPorts(compose).entries()) {
25
+ publishedPorts.push({ port, services: serviceNames });
26
+ }
27
+ collectDataMounts(compose, dataMounts);
28
+ collectRisks(compose, risks);
29
+ if (publishedPorts.length > 0) {
30
+ const primary = publishedPorts[0];
31
+ webHints.push(`偵測到 published port ${primary.port},可考慮在 synology-app.yaml 設定 adminport: ${primary.port}。`);
32
+ webHints.push("Web App 通常還需要 adminprotocol 與 adminurl。");
33
+ webHints.push("Launch Mode:integration.web.launch 為 dsm-window(預設,DSM 視窗)或 new-page(新分頁)。");
34
+ }
35
+ return { services, publishedPorts, dataMounts, risks, webHints };
36
+ }
37
+ function collectDataMounts(compose, out) {
38
+ if (!compose.services) {
39
+ return;
40
+ }
41
+ for (const [serviceName, service] of Object.entries(compose.services)) {
42
+ const volumes = normalizeVolumeEntries(service.volumes);
43
+ for (const volume of volumes) {
44
+ if (volume.includes("SYNOPKG_PKGVAR") || volume.includes("SYNO_APP_SECRETS_DIR")) {
45
+ out.push(`${serviceName}: ${volume}`);
46
+ }
47
+ }
48
+ }
49
+ }
50
+ function collectRisks(compose, out) {
51
+ if (!compose.services) {
52
+ return;
53
+ }
54
+ for (const [serviceName, service] of Object.entries(compose.services)) {
55
+ if (service.privileged) {
56
+ out.push(`${serviceName}: privileged`);
57
+ }
58
+ if (service.network_mode === "host") {
59
+ out.push(`${serviceName}: host network`);
60
+ }
61
+ if (Array.isArray(service.devices) && service.devices.length > 0) {
62
+ out.push(`${serviceName}: devices`);
63
+ }
64
+ if (Array.isArray(service.cap_add) && service.cap_add.length > 0) {
65
+ out.push(`${serviceName}: cap_add`);
66
+ }
67
+ }
68
+ }
69
+ function normalizeVolumeEntries(volumes) {
70
+ if (!volumes) {
71
+ return [];
72
+ }
73
+ const entries = Array.isArray(volumes) ? volumes : [volumes];
74
+ return entries
75
+ .map((entry) => (typeof entry === "string" ? entry : JSON.stringify(entry)))
76
+ .filter(Boolean);
77
+ }
78
+ export function formatInspectReport(report) {
79
+ const lines = [];
80
+ lines.push("Services:");
81
+ lines.push(report.services.length > 0 ? report.services.join(", ") : "(none)");
82
+ lines.push("");
83
+ lines.push("Published TCP ports:");
84
+ if (report.publishedPorts.length === 0) {
85
+ lines.push("(none)");
86
+ }
87
+ else {
88
+ for (const entry of report.publishedPorts) {
89
+ lines.push(`- ${entry.port} (${entry.services.join(", ")})`);
90
+ }
91
+ }
92
+ lines.push("");
93
+ lines.push("Data intents:");
94
+ lines.push(report.dataMounts.length > 0 ? report.dataMounts.join("\n") : "(none)");
95
+ lines.push("");
96
+ lines.push("Risk intents:");
97
+ lines.push(report.risks.length > 0 ? report.risks.join("\n") : "(none)");
98
+ lines.push("");
99
+ lines.push("Web entry hints:");
100
+ lines.push(report.webHints.length > 0 ? report.webHints.join("\n") : "(none)");
101
+ return lines.join("\n");
102
+ }
@@ -0,0 +1 @@
1
+ export declare function loadDotEnv(fromDir?: string): string | null;
@@ -0,0 +1,31 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ export function loadDotEnv(fromDir = process.cwd()) {
4
+ let current = resolve(fromDir);
5
+ while (true) {
6
+ const candidate = join(current, ".env");
7
+ if (existsSync(candidate)) {
8
+ for (const line of readFileSync(candidate, "utf8").split("\n")) {
9
+ const trimmed = line.trim();
10
+ if (!trimmed || trimmed.startsWith("#")) {
11
+ continue;
12
+ }
13
+ const eq = trimmed.indexOf("=");
14
+ if (eq <= 0) {
15
+ continue;
16
+ }
17
+ const key = trimmed.slice(0, eq).trim();
18
+ const value = trimmed.slice(eq + 1).trim();
19
+ if (process.env[key] === undefined) {
20
+ process.env[key] = value;
21
+ }
22
+ }
23
+ return candidate;
24
+ }
25
+ const parent = dirname(current);
26
+ if (parent === current) {
27
+ return null;
28
+ }
29
+ current = parent;
30
+ }
31
+ }
package/dist/log.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { ProjectRoot } from "./discovery.js";
2
+ import type { NasHost } from "./host/types.js";
3
+ export interface FollowProjectLogsOptions {
4
+ project: ProjectRoot;
5
+ host: NasHost;
6
+ dev?: boolean;
7
+ }
8
+ export interface FollowProjectLogsResult {
9
+ ok: boolean;
10
+ lines: string[];
11
+ diagnostics?: Record<string, unknown>;
12
+ }
13
+ export declare function followProjectLogs(options: FollowProjectLogsOptions): Promise<FollowProjectLogsResult>;
package/dist/log.js ADDED
@@ -0,0 +1,20 @@
1
+ import { resolveDeployIdentity } from "./deploy/identity.js";
2
+ export async function followProjectLogs(options) {
3
+ const identity = resolveDeployIdentity(options.project, { dev: options.dev });
4
+ const installed = await options.host.isPackageInstalled(identity.packageId);
5
+ if (!installed) {
6
+ return { ok: false, lines: [], diagnostics: { code: "NOT_INSTALLED" } };
7
+ }
8
+ const lines = await options.host.fetchContainerLogs(identity.packageId);
9
+ const info = await options.host.getAttestationInfo(identity.packageId);
10
+ return {
11
+ ok: true,
12
+ lines,
13
+ diagnostics: {
14
+ packageIdentity: identity.packageId,
15
+ attestationKind: info.attestationKind,
16
+ readiness: info.readiness,
17
+ redacted: true,
18
+ },
19
+ };
20
+ }
@@ -0,0 +1,2 @@
1
+ import type { BrokerBootstrap } from "@synopackageland/dev-sdk/node";
2
+ export declare function writeMockBrokerBootstrap(projectRoot: string, bootstrap: BrokerBootstrap): string;
@@ -0,0 +1,8 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ export function writeMockBrokerBootstrap(projectRoot, bootstrap) {
4
+ const path = resolve(projectRoot, ".synopkgland", "mock-broker.json");
5
+ mkdirSync(dirname(path), { recursive: true });
6
+ writeFileSync(path, `${JSON.stringify(bootstrap, null, 2)}\n`, "utf8");
7
+ return path;
8
+ }
@@ -0,0 +1,60 @@
1
+ import { createReadStream } from "node:fs";
2
+ export interface FileItem {
3
+ name: string;
4
+ type: "file" | "directory";
5
+ path: string;
6
+ size?: number;
7
+ }
8
+ export declare class FileStoreError extends Error {
9
+ readonly code: string;
10
+ readonly status: number;
11
+ constructor(code: string, status: number, message: string);
12
+ }
13
+ export interface MockFileStoreOptions {
14
+ filesRoot?: string;
15
+ recycleBinDisabled?: boolean;
16
+ }
17
+ export interface DownloadResult {
18
+ buffer?: Buffer;
19
+ stream?: ReturnType<typeof createReadStream>;
20
+ size: number;
21
+ totalSize: number;
22
+ }
23
+ export declare class MockFileStore {
24
+ private readonly filesRoot?;
25
+ private readonly recycleBinDisabled;
26
+ private readonly treeRoot;
27
+ constructor(options?: MockFileStoreOptions);
28
+ list(rawPath: string): FileItem[];
29
+ stat(rawPath: string): FileItem;
30
+ download(rawPath: string, range?: {
31
+ start: number;
32
+ end?: number;
33
+ }): DownloadResult;
34
+ upload(rawPath: string, body: Buffer, overwrite?: boolean): void;
35
+ mkdir(rawPath: string): void;
36
+ rename(rawPath: string, rawTo: string, overwrite?: boolean): void;
37
+ move(rawPath: string, rawTo: string, overwrite?: boolean): void;
38
+ copy(rawPath: string, rawTo: string, overwrite?: boolean): void;
39
+ trash(rawPath: string): void;
40
+ private resolveListPath;
41
+ private resolveOperationPath;
42
+ private listMemory;
43
+ private statMemory;
44
+ private downloadMemory;
45
+ private uploadMemory;
46
+ private mkdirMemory;
47
+ private moveMemory;
48
+ private copyMemory;
49
+ private trashMemory;
50
+ private listMapped;
51
+ private statMapped;
52
+ private downloadMapped;
53
+ private uploadMapped;
54
+ private mkdirMapped;
55
+ private moveMapped;
56
+ private copyMapped;
57
+ private trashMapped;
58
+ }
59
+ /** @deprecated Use MockFileStore.list instead */
60
+ export declare function listFakeFiles(path: string, filesRoot?: string): FileItem[];