@synopackageland/cli 0.3.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -9
- package/dist/bin.js +1 -2
- package/dist/build-spk/generator.d.ts +0 -3
- package/dist/build-spk/generator.js +135 -287
- package/dist/build-spk/native-lifecycle.js +0 -3
- package/dist/build-spk/native-payload.js +0 -1
- package/dist/cli.js +171 -151
- package/dist/compose/analyze.d.ts +1 -2
- package/dist/compose/analyze.js +5 -48
- package/dist/contract/unsupported-types.d.ts +10 -0
- package/dist/contract/unsupported-types.js +49 -0
- package/dist/contract/validate.js +25 -2
- package/dist/contract/web-launch.js +0 -2
- package/dist/deploy/deploy.d.ts +1 -4
- package/dist/deploy/deploy.js +57 -156
- package/dist/deploy/homes-values.js +1 -1
- package/dist/deploy/plan.d.ts +18 -0
- package/dist/deploy/plan.js +54 -0
- package/dist/deploy/port-preflight.d.ts +21 -0
- package/dist/deploy/port-preflight.js +42 -0
- package/dist/deploy/spk-version.d.ts +2 -14
- package/dist/deploy/spk-version.js +25 -111
- package/dist/dev.d.ts +1 -3
- package/dist/dev.js +14 -28
- package/dist/error-registry.js +810 -857
- package/dist/host/dsm-client.js +23 -263
- package/dist/host/dsm-port-profile.d.ts +47 -0
- package/dist/host/dsm-port-profile.js +206 -0
- package/dist/host/port-info-lookup.d.ts +9 -0
- package/dist/host/port-info-lookup.js +87 -0
- package/dist/host/port-info.d.ts +33 -0
- package/dist/host/port-info.js +66 -0
- package/dist/host/types.d.ts +6 -78
- package/dist/index.d.ts +0 -9
- package/dist/index.js +0 -6
- package/dist/info.d.ts +0 -1
- package/dist/info.js +3 -10
- package/dist/init.d.ts +11 -1
- package/dist/init.js +112 -4
- package/dist/log.js +2 -3
- package/dist/native/validate.js +0 -1
- package/dist/standalone/binary.d.ts +9 -0
- package/dist/standalone/binary.js +67 -0
- package/dist/standalone/build.d.ts +25 -0
- package/dist/standalone/build.js +108 -0
- package/dist/standalone/dsm-window.d.ts +26 -0
- package/dist/standalone/dsm-window.js +251 -0
- package/dist/standalone/helper.d.ts +31 -0
- package/dist/standalone/helper.js +104 -0
- package/dist/standalone/i18n.d.ts +28 -0
- package/dist/standalone/i18n.js +161 -0
- package/dist/standalone/inspect-spk.d.ts +21 -0
- package/dist/standalone/inspect-spk.js +160 -0
- package/dist/standalone/manifest.d.ts +71 -0
- package/dist/standalone/manifest.js +276 -0
- package/dist/standalone/root-messages.d.ts +7 -0
- package/dist/standalone/root-messages.js +250 -0
- package/dist/standalone/root-ops.d.ts +42 -0
- package/dist/standalone/root-ops.js +101 -0
- package/dist/standalone/root-window.d.ts +35 -0
- package/dist/standalone/root-window.js +290 -0
- package/dist/standalone/spk.d.ts +22 -0
- package/dist/standalone/spk.js +205 -0
- package/dist/templates/binary/synology-app.yaml +7 -0
- package/dist/templates/hello-files/compose.yaml +0 -1
- package/dist/templates/hello-web/compose.yaml +13 -10
- package/dist/templates/hello-web/html/app.js +1 -23
- package/dist/templates/hello-web/html/index.html +1 -3
- package/dist/templates/hello-web-page/compose.yaml +13 -10
- package/dist/templates/hello-web-page/html/app.js +1 -23
- package/dist/templates/hello-web-page/html/index.html +1 -3
- package/dist/templates/native-go/README.md +268 -0
- package/dist/templates/native-go/backend/cmd/app/main.go +49 -0
- package/dist/templates/native-go/backend/cmd/helper/main.go +38 -0
- package/dist/templates/native-go/backend/go.mod +3 -0
- package/dist/templates/native-go/backend/internal/api/errors.go +33 -0
- package/dist/templates/native-go/backend/internal/api/registry.go +94 -0
- package/dist/templates/native-go/backend/internal/api/registry_test.go +329 -0
- package/dist/templates/native-go/backend/internal/api/server.go +155 -0
- package/dist/templates/native-go/backend/internal/app/root_none.go +9 -0
- package/dist/templates/native-go/backend/internal/app/routes.go +127 -0
- package/dist/templates/native-go/backend/internal/app/routes_test.go +167 -0
- package/dist/templates/native-go/backend/internal/dsm/auth.go +118 -0
- package/dist/templates/native-go/backend/internal/dsm/auth_test.go +139 -0
- package/dist/templates/native-go/backend/internal/helper/control.go +219 -0
- package/dist/templates/native-go/backend/internal/helper/control_test.go +242 -0
- package/dist/templates/native-go/backend/internal/helper/peer_linux.go +44 -0
- package/dist/templates/native-go/backend/internal/helper/peer_other.go +7 -0
- package/dist/templates/native-go/backend/internal/helper/process_linux.go +167 -0
- package/dist/templates/native-go/backend/internal/helper/process_other.go +5 -0
- package/dist/templates/native-go/backend/internal/helper/server_linux.go +83 -0
- package/dist/templates/native-go/backend/internal/helper/status.go +271 -0
- package/dist/templates/native-go/backend/internal/helper/status_test.go +205 -0
- package/dist/templates/native-go/backend/internal/helper/supervision.go +84 -0
- package/dist/templates/native-go/backend/internal/helper/supervision_test.go +124 -0
- package/dist/templates/native-go/backend/internal/helper/supervisor_linux.go +192 -0
- package/dist/templates/native-go/backend/internal/helper/supervisor_linux_test.go +302 -0
- package/dist/templates/native-go/dsm-package.yaml +58 -0
- package/dist/templates/native-go/gitignore +1 -0
- package/dist/templates/native-go/i18n/glossary.json +662 -0
- package/dist/templates/native-go/i18n/messages/de.json +28 -0
- package/dist/templates/native-go/i18n/messages/en.json +28 -0
- package/dist/templates/native-go/i18n/messages/es.json +28 -0
- package/dist/templates/native-go/i18n/messages/fr.json +28 -0
- package/dist/templates/native-go/i18n/messages/it.json +28 -0
- package/dist/templates/native-go/i18n/messages/ja.json +28 -0
- package/dist/templates/native-go/i18n/messages/ko.json +28 -0
- package/dist/templates/native-go/i18n/messages/zh-CN.json +28 -0
- package/dist/templates/native-go/i18n/messages/zh-TW.json +28 -0
- package/dist/templates/native-go/ui/app.css +35 -0
- package/dist/templates/native-go/ui/app.js +94 -0
- package/dist/templates/native-go-better-auth/backend/internal/app/identity.go +208 -0
- package/dist/templates/native-go-better-auth/backend/internal/app/identity_test.go +113 -0
- package/dist/templates/native-go-better-auth/node/app.mjs +72 -0
- package/dist/templates/native-go-better-auth/node/build.mjs +2 -0
- package/dist/templates/native-go-better-auth/node/package-lock.json +862 -0
- package/dist/templates/native-go-better-auth/node/package.json +1 -0
- package/dist/templates/native-go-better-auth/node/test/auth.test.mjs +31 -0
- package/dist/templates/native-go-integration/backend/internal/app/identity_none.go +6 -0
- package/dist/templates/native-go-integration/backend/internal/app/personal.go +203 -0
- package/dist/templates/native-go-integration/backend/internal/app/personal_test.go +119 -0
- package/dist/templates/native-go-root/backend/internal/app/root.go +107 -0
- package/dist/templates/native-go-root/backend/internal/app/root_test.go +124 -0
- package/dist/templates/native-go-root/root/whoami.sh +6 -0
- package/dist/templates/native-go-vue/README.md +110 -0
- package/dist/templates/native-go-vue/frontend/DESIGN.md +78 -0
- package/dist/templates/native-go-vue/frontend/package-lock.json +2507 -0
- package/dist/templates/native-go-vue/frontend/package.json +6 -0
- package/dist/templates/native-go-vue/frontend/src/App.vue +132 -0
- package/dist/templates/native-go-vue/frontend/src/context.ts +17 -0
- package/dist/templates/native-go-vue/frontend/src/main.ts +26 -0
- package/dist/templates/native-go-vue/frontend/src/style.css +14 -0
- package/dist/templates/native-go-vue/frontend/test/apps.test.ts +148 -0
- package/dist/templates/native-go-vue/frontend/tsconfig.json +1 -0
- package/dist/templates/native-go-vue/frontend/vite.config.ts +8 -0
- package/dist/templates/native-go-vue/gitignore +2 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate.js +11 -7
- package/package.json +3 -7
- package/schema/synology-app.schema.json +36 -37
- package/skills/SKILL.md +4 -48
- package/skills/reference/create.md +11 -83
- package/skills/reference/deploy.md +7 -78
- package/skills/reference/diagnose.md +4 -101
- package/skills/reference/files.md +5 -41
- package/skills/reference/identity.md +3 -29
- package/skills/reference/wrap.md +0 -2
- package/templates/binary/synology-app.yaml +7 -0
- package/templates/hello-files/compose.yaml +0 -1
- package/templates/hello-web/compose.yaml +13 -10
- package/templates/hello-web/html/app.js +1 -23
- package/templates/hello-web/html/index.html +1 -3
- package/templates/hello-web-page/compose.yaml +13 -10
- package/templates/hello-web-page/html/app.js +1 -23
- package/templates/hello-web-page/html/index.html +1 -3
- package/templates/native-go/README.md +268 -0
- package/templates/native-go/backend/cmd/app/main.go +49 -0
- package/templates/native-go/backend/cmd/helper/main.go +38 -0
- package/templates/native-go/backend/go.mod +3 -0
- package/templates/native-go/backend/internal/api/errors.go +33 -0
- package/templates/native-go/backend/internal/api/registry.go +94 -0
- package/templates/native-go/backend/internal/api/registry_test.go +329 -0
- package/templates/native-go/backend/internal/api/server.go +155 -0
- package/templates/native-go/backend/internal/app/root_none.go +9 -0
- package/templates/native-go/backend/internal/app/routes.go +127 -0
- package/templates/native-go/backend/internal/app/routes_test.go +167 -0
- package/templates/native-go/backend/internal/dsm/auth.go +118 -0
- package/templates/native-go/backend/internal/dsm/auth_test.go +139 -0
- package/templates/native-go/backend/internal/helper/control.go +219 -0
- package/templates/native-go/backend/internal/helper/control_test.go +242 -0
- package/templates/native-go/backend/internal/helper/peer_linux.go +44 -0
- package/templates/native-go/backend/internal/helper/peer_other.go +7 -0
- package/templates/native-go/backend/internal/helper/process_linux.go +167 -0
- package/templates/native-go/backend/internal/helper/process_other.go +5 -0
- package/templates/native-go/backend/internal/helper/server_linux.go +83 -0
- package/templates/native-go/backend/internal/helper/status.go +271 -0
- package/templates/native-go/backend/internal/helper/status_test.go +205 -0
- package/templates/native-go/backend/internal/helper/supervision.go +84 -0
- package/templates/native-go/backend/internal/helper/supervision_test.go +124 -0
- package/templates/native-go/backend/internal/helper/supervisor_linux.go +192 -0
- package/templates/native-go/backend/internal/helper/supervisor_linux_test.go +302 -0
- package/templates/native-go/dsm-package.yaml +58 -0
- package/templates/native-go/gitignore +1 -0
- package/templates/native-go/i18n/glossary.json +662 -0
- package/templates/native-go/i18n/messages/de.json +28 -0
- package/templates/native-go/i18n/messages/en.json +28 -0
- package/templates/native-go/i18n/messages/es.json +28 -0
- package/templates/native-go/i18n/messages/fr.json +28 -0
- package/templates/native-go/i18n/messages/it.json +28 -0
- package/templates/native-go/i18n/messages/ja.json +28 -0
- package/templates/native-go/i18n/messages/ko.json +28 -0
- package/templates/native-go/i18n/messages/zh-CN.json +28 -0
- package/templates/native-go/i18n/messages/zh-TW.json +28 -0
- package/templates/native-go/ui/app.css +35 -0
- package/templates/native-go/ui/app.js +94 -0
- package/templates/native-go-better-auth/backend/internal/app/identity.go +208 -0
- package/templates/native-go-better-auth/backend/internal/app/identity_test.go +113 -0
- package/templates/native-go-better-auth/node/app.mjs +72 -0
- package/templates/native-go-better-auth/node/build.mjs +2 -0
- package/templates/native-go-better-auth/node/package-lock.json +862 -0
- package/templates/native-go-better-auth/node/package.json +1 -0
- package/templates/native-go-better-auth/node/test/auth.test.mjs +31 -0
- package/templates/native-go-integration/backend/internal/app/identity_none.go +6 -0
- package/templates/native-go-integration/backend/internal/app/personal.go +203 -0
- package/templates/native-go-integration/backend/internal/app/personal_test.go +119 -0
- package/templates/native-go-root/backend/internal/app/root.go +107 -0
- package/templates/native-go-root/backend/internal/app/root_test.go +124 -0
- package/templates/native-go-root/root/whoami.sh +6 -0
- package/templates/native-go-vue/README.md +110 -0
- package/templates/native-go-vue/frontend/DESIGN.md +78 -0
- package/templates/native-go-vue/frontend/package-lock.json +2507 -0
- package/templates/native-go-vue/frontend/package.json +6 -0
- package/templates/native-go-vue/frontend/src/App.vue +132 -0
- package/templates/native-go-vue/frontend/src/context.ts +17 -0
- package/templates/native-go-vue/frontend/src/main.ts +26 -0
- package/templates/native-go-vue/frontend/src/style.css +14 -0
- package/templates/native-go-vue/frontend/test/apps.test.ts +148 -0
- package/templates/native-go-vue/frontend/tsconfig.json +1 -0
- package/templates/native-go-vue/frontend/vite.config.ts +8 -0
- package/templates/native-go-vue/gitignore +2 -0
- package/dist/attestation/app-package.d.ts +0 -86
- package/dist/attestation/app-package.js +0 -206
- package/dist/attestation/install-on-host.d.ts +0 -14
- package/dist/attestation/install-on-host.js +0 -52
- package/dist/attestation/keyring.d.ts +0 -21
- package/dist/attestation/keyring.js +0 -168
- package/dist/attestation/platform-service.d.ts +0 -70
- package/dist/attestation/platform-service.js +0 -250
- package/dist/build-spk/broker-glue.d.ts +0 -16
- package/dist/build-spk/broker-glue.js +0 -40
- package/dist/deploy/catalog-publish.d.ts +0 -3
- package/dist/deploy/catalog-publish.js +0 -7
- package/dist/doctor.d.ts +0 -22
- package/dist/doctor.js +0 -721
- package/dist/error-collector.d.ts +0 -2
- package/dist/error-collector.js +0 -46
- package/dist/host/broker-paths.d.ts +0 -1
- package/dist/host/broker-paths.js +0 -1
- package/dist/host/broker-upgrade-preflight.d.ts +0 -61
- package/dist/host/broker-upgrade-preflight.js +0 -156
- package/dist/host/digests.d.ts +0 -5
- package/dist/host/digests.js +0 -63
- package/dist/host/fake-nas.d.ts +0 -41
- package/dist/host/fake-nas.js +0 -174
- package/dist/mock-broker/bootstrap.d.ts +0 -2
- package/dist/mock-broker/bootstrap.js +0 -8
- package/dist/mock-broker/files.d.ts +0 -60
- package/dist/mock-broker/files.js +0 -408
- package/dist/mock-broker/oidc.d.ts +0 -31
- package/dist/mock-broker/oidc.js +0 -161
- package/dist/mock-broker/path.d.ts +0 -9
- package/dist/mock-broker/path.js +0 -50
- package/dist/mock-broker/server.d.ts +0 -19
- package/dist/mock-broker/server.js +0 -350
- package/dist/publish/build-ledger.d.ts +0 -22
- package/dist/publish/build-ledger.js +0 -26
- package/dist/publish/community-path.d.ts +0 -6
- package/dist/publish/community-path.js +0 -41
- package/dist/publish/curated-release.d.ts +0 -78
- package/dist/publish/curated-release.js +0 -883
- package/dist/publish/file-build-ledger.d.ts +0 -18
- package/dist/publish/file-build-ledger.js +0 -268
- package/dist/publish/github-pr.d.ts +0 -25
- package/dist/publish/github-pr.js +0 -85
- package/dist/publish/github-repo.d.ts +0 -8
- package/dist/publish/github-repo.js +0 -21
- package/dist/publish/platform-service-release.d.ts +0 -60
- package/dist/publish/platform-service-release.js +0 -333
- package/dist/publish/preflight.d.ts +0 -24
- package/dist/publish/preflight.js +0 -138
- package/dist/publish/publish.d.ts +0 -27
- package/dist/publish/publish.js +0 -136
- package/dist/publish/sync-definition.d.ts +0 -4
- package/dist/publish/sync-definition.js +0 -48
- package/dist/release-bin.d.ts +0 -2
- package/dist/release-bin.js +0 -23
- package/dist/templates/hello-web/html/sdk.js +0 -68
- package/dist/templates/hello-web-page/html/sdk.js +0 -68
- package/dist/templates/native/synology-app.yaml +0 -7
- package/dist/test-host.d.ts +0 -15
- package/dist/test-host.js +0 -37
- package/templates/hello-web/html/sdk.js +0 -68
- package/templates/hello-web-page/html/sdk.js +0 -68
- package/templates/native/synology-app.yaml +0 -7
- /package/dist/templates/{native → binary}/native.yaml +0 -0
- /package/templates/{native → binary}/native.yaml +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "native-dsm-vue-app", "private": true, "type": "module",
|
|
3
|
+
"scripts": {"build": "vue-tsc --noEmit && vite build", "test": "vitest run"},
|
|
4
|
+
"dependencies": {"@synopackageland/ui": "0.4.0", "vue": "3.5.43"},
|
|
5
|
+
"devDependencies": {"@vitejs/plugin-vue": "6.0.9", "vite": "7.3.6", "typescript": "5.9.3", "vue-tsc": "3.3.11", "vitest": "3.2.4", "jsdom": "26.1.0"}
|
|
6
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {computed, onMounted, onUnmounted, ref} from 'vue'
|
|
3
|
+
import {AppRoot, Button, Callout, Dialog, SettingsSection, Textarea} from '@synopackageland/ui'
|
|
4
|
+
import {stateCopy, type AppStatus, type Context, type Status} from './context'
|
|
5
|
+
const props = defineProps<{ctx: Context}>()
|
|
6
|
+
const app = ref<AppStatus>()
|
|
7
|
+
const status = ref<Status>()
|
|
8
|
+
const busy = ref(false)
|
|
9
|
+
const error = ref('')
|
|
10
|
+
const notice = ref('')
|
|
11
|
+
const disableOpen = ref(false)
|
|
12
|
+
const id = ref('')
|
|
13
|
+
const text = ref('')
|
|
14
|
+
const identity = ref('')
|
|
15
|
+
let disposed = false
|
|
16
|
+
const refreshing = ref(false)
|
|
17
|
+
let appCookie = ''
|
|
18
|
+
const configured = computed(() => status.value?.reason !== "not-requested")
|
|
19
|
+
const details = computed(() => status.value ? stateCopy[status.value.state] : undefined)
|
|
20
|
+
function message(e: unknown) {
|
|
21
|
+
const code = (e as {code?: string})?.code || 'request_failed'
|
|
22
|
+
return ({session_required:'Your DSM session expired. Sign in again.',access_denied:'This sample belongs to another account.',integration_unavailable:'Integration is unavailable. Ask an administrator to activate or recover it.',personal_unavailable:'Sample storage is unavailable. Ask an administrator to check package data.',root_conflict:'A conflicting scheduler task exists. Ask an administrator to review it.',root_verify_failed:'Scheduler verification failed. Refresh and review the task.',root_run_failed:'The approved operation failed. Review integration status and cleanup.'} as Record<string,string>)[code] || `Request failed (${code}). Refresh and try again.`
|
|
23
|
+
}
|
|
24
|
+
async function refresh() {
|
|
25
|
+
if(disposed || refreshing.value) return
|
|
26
|
+
refreshing.value = true; error.value = ''
|
|
27
|
+
try {
|
|
28
|
+
const [a,s] = await Promise.all([props.ctx.call<AppStatus>('app.status'),props.ctx.call<Status>('integration.status')])
|
|
29
|
+
if(disposed) return
|
|
30
|
+
app.value = a; status.value = s
|
|
31
|
+
if(s.state !== 'ready') {id.value='';text.value='';return}
|
|
32
|
+
// Plain --ui vue has no personal routes; not-enabled status hides this.
|
|
33
|
+
const list = await props.ctx.call<{items:Array<{id:string;name:string}>}>('personal.list')
|
|
34
|
+
if(disposed) return
|
|
35
|
+
id.value = list.items[0]?.id || ''
|
|
36
|
+
if(id.value) {
|
|
37
|
+
const note = await props.ctx.call<{text:string}>('personal.read',{id:id.value})
|
|
38
|
+
if(!disposed) text.value = note.text
|
|
39
|
+
}
|
|
40
|
+
} catch(e) {if(!disposed) {error.value = message(e);status.value=undefined;id.value='';text.value=''}} finally {refreshing.value=false}
|
|
41
|
+
}
|
|
42
|
+
async function action(op: string) {
|
|
43
|
+
if(busy.value || refreshing.value || disposed) return
|
|
44
|
+
busy.value=true;error.value='';notice.value=''
|
|
45
|
+
try {
|
|
46
|
+
if(!props.ctx.root) throw {code:'unsupported'}
|
|
47
|
+
const approved = await props.ctx.root.authorize(op)
|
|
48
|
+
if(disposed) return
|
|
49
|
+
if(approved !== 'ready') {notice.value='Approval cancelled. No operation was run.';return}
|
|
50
|
+
await props.ctx.root.run(op)
|
|
51
|
+
if(disposed) return
|
|
52
|
+
notice.value='Operation completed. Integration status was checked again.'
|
|
53
|
+
await refresh()
|
|
54
|
+
} catch(e) {if(!disposed) {await refresh();if(!disposed) error.value=message(e)}}
|
|
55
|
+
finally {if(!disposed) busy.value=false}
|
|
56
|
+
}
|
|
57
|
+
async function save() {
|
|
58
|
+
if(busy.value || refreshing.value || !id.value || disposed) return
|
|
59
|
+
busy.value=true;error.value='';notice.value=''
|
|
60
|
+
try {await props.ctx.call('personal.write',{id:id.value,text:text.value});if(!disposed) notice.value='Your sample note was saved.'}
|
|
61
|
+
catch(e) {if(!disposed) error.value=message(e)} finally {if(!disposed) busy.value=false}
|
|
62
|
+
}
|
|
63
|
+
async function login() {
|
|
64
|
+
if(busy.value || refreshing.value || disposed) return
|
|
65
|
+
busy.value=true;error.value=''
|
|
66
|
+
try {
|
|
67
|
+
const result=await props.ctx.call<{cookie:string;user:string}>('identity.login')
|
|
68
|
+
if(!disposed){appCookie=result.cookie;identity.value=`App session for ${result.user}`}
|
|
69
|
+
} catch(e) {if(!disposed) error.value=message(e)} finally {if(!disposed) busy.value=false}
|
|
70
|
+
}
|
|
71
|
+
async function checkIdentity() {
|
|
72
|
+
if(busy.value || refreshing.value || disposed) return
|
|
73
|
+
busy.value=true;error.value=''
|
|
74
|
+
try {const result=await props.ctx.call<{user:string}>('identity.current',{cookie:appCookie});if(!disposed) identity.value=`App session for ${result.user}`}
|
|
75
|
+
catch(e) {if(!disposed) {appCookie='';identity.value='';error.value=message(e)}} finally {if(!disposed) busy.value=false}
|
|
76
|
+
}
|
|
77
|
+
const hasIdentity = ref(false)
|
|
78
|
+
onMounted(async()=>{
|
|
79
|
+
props.ctx.reopen(()=>{if(!disposed) void refresh()})
|
|
80
|
+
await refresh()
|
|
81
|
+
if(disposed || !app.value) return
|
|
82
|
+
try {const result = await props.ctx.call<{mode:string}>('identity.status');if(!disposed) hasIdentity.value = result.mode==='better-auth'} catch { /* Optional route absent in Go-only. */ }
|
|
83
|
+
})
|
|
84
|
+
onUnmounted(()=>{disposed=true;appCookie='';text.value='';id.value=''})
|
|
85
|
+
</script>
|
|
86
|
+
|
|
87
|
+
<template>
|
|
88
|
+
<AppRoot modal-scope="app" style="height: 100%">
|
|
89
|
+
<main class="integration-app" :aria-busy="busy || refreshing" aria-label="Package settings">
|
|
90
|
+
<header class="integration-heading">
|
|
91
|
+
<div><h1>{{ app?.package || 'Package settings' }}</h1><p v-if="app">{{ app.user }} · {{ app.version }}</p></div>
|
|
92
|
+
<Button variant="secondary" :disabled="busy || refreshing" @click="error=''; refresh()">Refresh</Button>
|
|
93
|
+
</header>
|
|
94
|
+
<p v-if="refreshing" role="status">Refreshing status…</p>
|
|
95
|
+
<p v-else-if="busy" role="status">Completing operation…</p>
|
|
96
|
+
<Callout v-if="error" tone="danger" title="Action required"><span role="alert">{{ error }}</span></Callout>
|
|
97
|
+
<p v-if="notice" class="integration-notice" role="status">{{ notice }}</p>
|
|
98
|
+
<SettingsSection title="DSM integration" :collapsible="false">
|
|
99
|
+
<div v-if="status && details" :data-integration-state="status.state" class="integration-status" role="status">
|
|
100
|
+
<h2>{{ details.label }}</h2><p>{{ details.description }}</p>
|
|
101
|
+
<p v-if="status.cleanup === 'required'">Cleanup is required. Ask an administrator to run teardown or disable.</p>
|
|
102
|
+
<dl><dt>Package running</dt><dd>{{ status.packageRunning }}</dd><dt>Scheduler</dt><dd>{{ status.scheduler }}</dd></dl>
|
|
103
|
+
</div>
|
|
104
|
+
<p v-else-if="error" role="status">Status could not be loaded. Refresh to try again.</p>
|
|
105
|
+
<p v-else role="status">Loading integration status…</p>
|
|
106
|
+
<div v-if="app?.admin && ctx.root && configured" class="integration-actions">
|
|
107
|
+
<Button variant="primary" :disabled="busy || refreshing" @click="action('helper_enable')">Approve and activate</Button>
|
|
108
|
+
<Button variant="secondary" :disabled="busy || refreshing" @click="action('helper_recover')">Recover</Button>
|
|
109
|
+
<Button variant="secondary" :disabled="busy || refreshing" @click="action('helper_teardown')">Teardown</Button>
|
|
110
|
+
<Button variant="secondary" :disabled="busy || refreshing" @click="disableOpen=true">Disable</Button>
|
|
111
|
+
</div>
|
|
112
|
+
<Callout v-if="app && !app.admin && status?.state !== 'ready'" title="Administrator action needed">Ask a DSM administrator to open this package's settings. Your login cannot approve root operations.</Callout>
|
|
113
|
+
</SettingsSection>
|
|
114
|
+
<SettingsSection v-if="status?.state === 'ready'" title="My sample data" :collapsible="false">
|
|
115
|
+
<p class="integration-help">This synthetic note belongs to your DSM account and is stored in package data.</p>
|
|
116
|
+
<p v-if="refreshing" role="status">Loading your sample data…</p>
|
|
117
|
+
<Callout v-else-if="!id" title="No sample data">Refresh to request your sample data again.</Callout>
|
|
118
|
+
<label v-if="id" class="integration-field"><span>My sample note</span><Textarea v-model="text" aria-label="My sample note" :disabled="busy || refreshing" /></label>
|
|
119
|
+
<Button v-if="id" variant="primary" :disabled="busy || refreshing" @click="save">Save my note</Button>
|
|
120
|
+
</SettingsSection>
|
|
121
|
+
<SettingsSection v-if="hasIdentity" title="Optional app identity" :collapsible="false">
|
|
122
|
+
<p class="integration-help">Your DSM login is checked again for every app session request.</p>
|
|
123
|
+
<div class="integration-actions"><Button variant="primary" :disabled="busy || refreshing" @click="login">Create app session</Button><Button variant="secondary" :disabled="busy || refreshing || !identity" @click="checkIdentity">Check app session</Button></div>
|
|
124
|
+
<p v-if="identity" role="status">{{ identity }}</p>
|
|
125
|
+
</SettingsSection>
|
|
126
|
+
<Dialog v-model:open="disableOpen" title="Disable integration?" description="New integration requests will be refused before helper cleanup begins.">
|
|
127
|
+
<p>Your sample data and historical root output are retained. A failed cleanup remains visible.</p>
|
|
128
|
+
<template #footer><Button variant="secondary" @click="disableOpen=false">Cancel</Button><Button variant="danger" :disabled="busy || refreshing" @click="disableOpen=false; action('helper_disable')">Confirm disable</Button></template>
|
|
129
|
+
</Dialog>
|
|
130
|
+
</main>
|
|
131
|
+
</AppRoot>
|
|
132
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type IntegrationState = 'not-enabled' | 'ready' | 'needs-approval' | 'incompatible' | 'stopped' | 'failed'
|
|
2
|
+
export interface Status {state: IntegrationState; reason?: string; cleanup?: string; packageRunning: string; scheduler: string}
|
|
3
|
+
export interface Context {
|
|
4
|
+
call<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>
|
|
5
|
+
reopen(fn: () => void): void
|
|
6
|
+
locale: string
|
|
7
|
+
root?: {state(op: string): Promise<string>; authorize(op: string): Promise<string>; run(op: string): Promise<unknown>; remove(op: string): Promise<string>}
|
|
8
|
+
}
|
|
9
|
+
export interface AppStatus {package: string; version: string; user: string; admin: boolean}
|
|
10
|
+
export const stateCopy: Record<IntegrationState, {label: string; description: string}> = {
|
|
11
|
+
'not-enabled': {label: 'Not enabled', description: 'An administrator can enable integration for this package.'},
|
|
12
|
+
ready: {label: 'Ready', description: 'The helper passed a fresh trusted health check.'},
|
|
13
|
+
'needs-approval': {label: 'Approval required', description: 'Ask an administrator to approve and activate integration.'},
|
|
14
|
+
incompatible: {label: 'Incompatible', description: 'Inventory or trust checks failed. Ask an administrator to review this package.'},
|
|
15
|
+
stopped: {label: 'Stopped', description: 'An administrator must recover integration after start or reboot.'},
|
|
16
|
+
failed: {label: 'Failed', description: 'Integration could not finish. Review cleanup status before retrying.'},
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {createApp} from 'vue'
|
|
2
|
+
import App from './App.vue'
|
|
3
|
+
import type {Context} from './context'
|
|
4
|
+
import '@synopackageland/ui/themes/dsm7-embedded.css'
|
|
5
|
+
import './style.css'
|
|
6
|
+
|
|
7
|
+
let mountSequence = 0
|
|
8
|
+
export function mount(root: HTMLElement, ctx: Context) {
|
|
9
|
+
const app = createApp(App, {ctx})
|
|
10
|
+
// Include the generated package ID, an instance counter and randomness even
|
|
11
|
+
// across separately loaded bundles. getRandomValues also works on DSM HTTP.
|
|
12
|
+
app.config.idPrefix = `__PACKAGE__-${++mountSequence}-${crypto.getRandomValues(new Uint32Array(4)).join('-')}`
|
|
13
|
+
const hadRootClass = root.classList.contains('dsmapp-vue-root')
|
|
14
|
+
root.classList.add('dsmapp-vue-root')
|
|
15
|
+
app.mount(root)
|
|
16
|
+
return {unmount() {app.unmount(); if(!hadRootClass) root.classList.remove('dsmapp-vue-root')}}
|
|
17
|
+
}
|
|
18
|
+
// Register with the existing per-package DSM window loader. Never share a
|
|
19
|
+
// singleton app or user/session state between different mount calls.
|
|
20
|
+
const script = document.currentScript as HTMLScriptElement | null
|
|
21
|
+
const packageID = script?.getAttribute('data-package')
|
|
22
|
+
const host = window as unknown as {DsmPackageApps: Record<string, {mount: typeof mount}>}
|
|
23
|
+
if (packageID) {
|
|
24
|
+
host.DsmPackageApps = host.DsmPackageApps || {}
|
|
25
|
+
host.DsmPackageApps[packageID] = {mount}
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
.dsmapp-vue-root {height: 100%; min-height: 0; width: 100%; overflow: hidden;}
|
|
2
|
+
[data-ui-app] .integration-app {padding: var(--ui-space-5, 20px); max-width: 72ch; color: var(--ui-text); font-family: var(--ui-font-sans); font-size: 13px; line-height: 1.5;}
|
|
3
|
+
[data-ui-app] .integration-heading {display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px;}
|
|
4
|
+
[data-ui-app] .integration-heading h1 {font-size: 20px; font-weight: 600; margin: 0;}
|
|
5
|
+
[data-ui-app] .integration-heading p {color: var(--ui-text); margin: 4px 0 0;}
|
|
6
|
+
[data-ui-app] .integration-app section {margin-top: 24px;}
|
|
7
|
+
[data-ui-app] .integration-status h2 {font-size: 15px; font-weight: 600; margin: 0;}
|
|
8
|
+
[data-ui-app] .integration-status p {margin: 6px 0;}
|
|
9
|
+
[data-ui-app] .integration-status dl {display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; margin: 12px 0;}
|
|
10
|
+
[data-ui-app] .integration-status dt, [data-ui-app] .integration-help {color: var(--ui-text);}
|
|
11
|
+
[data-ui-app] .integration-status dd {margin: 0;}
|
|
12
|
+
[data-ui-app] .integration-actions {display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px;}
|
|
13
|
+
[data-ui-app] .integration-field {display: grid; gap: 6px; margin: 12px 0;}
|
|
14
|
+
[data-ui-app] .integration-notice {color: var(--ui-text);}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import {expect, test, vi} from 'vitest'
|
|
3
|
+
import {nextTick} from 'vue'
|
|
4
|
+
import {mount} from '../src/main'
|
|
5
|
+
import type {Context, IntegrationState} from '../src/context'
|
|
6
|
+
|
|
7
|
+
function fixture(id: string, admin: boolean, state: IntegrationState = 'ready') {
|
|
8
|
+
const reopen: Array<() => void> = []
|
|
9
|
+
const call = vi.fn(async (method: string) => {
|
|
10
|
+
if(method === 'app.status') return {package: id, version:'0.1.0-0001',user:admin?'admin':'normal1',admin}
|
|
11
|
+
if(method === 'integration.status') return {state,reason:'fixture',packageRunning:'unknown',scheduler:'not-probed'}
|
|
12
|
+
if(method === 'personal.list') return {items:[{id:`${id}-private`,name:'My sample note'}]}
|
|
13
|
+
if(method === 'personal.read') return {id:`${id}-private`,text:`Private ${id}`}
|
|
14
|
+
throw {code:'unknown_method'}
|
|
15
|
+
})
|
|
16
|
+
const ctx = {call,reopen:(fn:()=>void)=>reopen.push(fn),locale:'en',root:{state:vi.fn(async()=> 'missing'),authorize:vi.fn(async()=> 'ready'),run:vi.fn(async()=>{}),remove:vi.fn(async()=> 'missing')}} as Context
|
|
17
|
+
const root=document.createElement('div');document.body.append(root)
|
|
18
|
+
const app=mount(root,ctx)
|
|
19
|
+
return {root,app,ctx,call,reopen}
|
|
20
|
+
}
|
|
21
|
+
async function settle(){for(let i=0;i<12;i++){await Promise.resolve();await nextTick()}await new Promise(resolve=>setTimeout(resolve,25))}
|
|
22
|
+
function click(root:HTMLElement,text:string){const node=[...root.querySelectorAll('button')].find(node=>node.textContent?.trim()===text);expect(node, text).toBeTruthy();node!.click()}
|
|
23
|
+
|
|
24
|
+
test('six states and ordinary-user errors give actionable status without approval controls',async()=>{
|
|
25
|
+
for(const state of ['not-enabled','ready','needs-approval','incompatible','stopped','failed'] as IntegrationState[]){
|
|
26
|
+
const f=fixture('Ordinary',false,state);await settle()
|
|
27
|
+
expect(f.root.querySelector('[data-integration-state]')?.getAttribute('data-integration-state')).toBe(state)
|
|
28
|
+
expect(f.root.textContent).not.toContain('Approve and activate')
|
|
29
|
+
f.app.unmount();f.root.remove()
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
test('approval cancellation never runs the root task and all fixed controls recheck status',async()=>{
|
|
33
|
+
const f=fixture('Admin',true,'needs-approval');await settle()
|
|
34
|
+
f.ctx.root!.authorize=vi.fn(async()=> 'cancelled')
|
|
35
|
+
click(f.root,'Approve and activate');await settle()
|
|
36
|
+
expect(f.ctx.root!.run).not.toHaveBeenCalled()
|
|
37
|
+
expect(f.root.textContent).toContain('Approval cancelled')
|
|
38
|
+
f.ctx.root!.authorize=vi.fn(async()=> 'ready')
|
|
39
|
+
click(f.root,'Approve and activate');await settle()
|
|
40
|
+
expect(f.ctx.root!.run).toHaveBeenCalledWith('helper_enable')
|
|
41
|
+
for(const [label,op] of [['Recover','helper_recover'],['Teardown','helper_teardown']] as const){click(f.root,label);await settle();expect(f.ctx.root!.run).toHaveBeenCalledWith(op)}
|
|
42
|
+
click(f.root,'Disable');await settle();click(f.root,'Confirm disable');await settle();expect(f.ctx.root!.run).toHaveBeenCalledWith('helper_disable')
|
|
43
|
+
f.app.unmount();f.root.remove()
|
|
44
|
+
})
|
|
45
|
+
test('two generated package IDs keep mounts, reopen events, overlays and unmount independent',async()=>{
|
|
46
|
+
const a=fixture('M1bAlpha',true),b=fixture('M1bBeta',true);await settle()
|
|
47
|
+
expect(a.root.textContent).toContain('M1bAlpha');expect(a.root.textContent).not.toContain('M1bBeta')
|
|
48
|
+
expect(b.root.textContent).toContain('M1bBeta')
|
|
49
|
+
click(a.root,'Disable');await settle()
|
|
50
|
+
expect(a.root.querySelector('[role="dialog"]')).toBeTruthy()
|
|
51
|
+
expect(b.root.querySelector('[role="dialog"]')).toBeNull()
|
|
52
|
+
const ac=a.call.mock.calls.length,bc=b.call.mock.calls.length
|
|
53
|
+
a.reopen.forEach(fn=>fn());await settle();expect(a.call.mock.calls.length).toBeGreaterThan(ac);expect(b.call.mock.calls.length).toBe(bc)
|
|
54
|
+
a.app.unmount();await settle();expect(a.root.childElementCount).toBe(0)
|
|
55
|
+
const count=a.call.mock.calls.length;a.reopen.forEach(fn=>fn());await settle();expect(a.call.mock.calls.length).toBe(count)
|
|
56
|
+
b.reopen.forEach(fn=>fn());await settle();expect(b.call.mock.calls.length).toBeGreaterThan(bc)
|
|
57
|
+
b.app.unmount();a.root.remove();b.root.remove()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('first-load errors replace loading text with an explicit retry state',async()=>{
|
|
61
|
+
const root=document.createElement('div');document.body.append(root)
|
|
62
|
+
const ctx={locale:'en',reopen(){},call:vi.fn(async()=>{throw {code:'auth_unavailable'}})} as Context
|
|
63
|
+
const app=mount(root,ctx);await settle()
|
|
64
|
+
expect(root.textContent).toContain('Status could not be loaded')
|
|
65
|
+
expect(root.textContent).not.toContain('Loading integration status')
|
|
66
|
+
app.unmount();root.remove()
|
|
67
|
+
})
|
|
68
|
+
test('a ready integration with no sample handle shows an explicit empty state',async()=>{
|
|
69
|
+
const f=fixture('Empty',false)
|
|
70
|
+
f.call.mockImplementation(async(method:string)=> {
|
|
71
|
+
if(method==='app.status')return {package:'Empty',version:'0.1.0',user:'normal1',admin:false}
|
|
72
|
+
if(method==='integration.status')return {state:'ready',packageRunning:'unknown',scheduler:'not-probed'}
|
|
73
|
+
if(method==='personal.list')return {items:[]}
|
|
74
|
+
throw {code:'unknown_method'}
|
|
75
|
+
})
|
|
76
|
+
f.reopen.forEach(fn=>fn());await settle()
|
|
77
|
+
// Reopen once after the initial in-flight refresh has finished.
|
|
78
|
+
f.reopen.forEach(fn=>fn());await settle()
|
|
79
|
+
expect(f.root.textContent).toContain('No sample data')
|
|
80
|
+
f.app.unmount();f.root.remove()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('root failure diagnostics survive the required post-failure status refresh',async()=>{
|
|
84
|
+
const f=fixture('RootFailure',true,'needs-approval');await settle()
|
|
85
|
+
f.ctx.root!.run=vi.fn(async()=>{throw {code:'root_run_failed'}})
|
|
86
|
+
click(f.root,'Approve and activate');await settle()
|
|
87
|
+
expect(f.root.textContent).toContain('The approved operation failed')
|
|
88
|
+
expect(f.call.mock.calls.filter(args=>args[0]==='integration.status').length).toBeGreaterThan(1)
|
|
89
|
+
f.app.unmount();f.root.remove()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('a local modal leaves the other mount interactive and preserves host styles',async()=>{
|
|
93
|
+
const a=fixture('M1bAlpha',true),b=fixture('M1bBeta',true)
|
|
94
|
+
const body=document.body.getAttribute('style'),html=document.documentElement.getAttribute('style')
|
|
95
|
+
try {
|
|
96
|
+
await settle();click(a.root,'Disable');await settle()
|
|
97
|
+
const dialog=a.root.querySelector('[role="dialog"]')!
|
|
98
|
+
expect(dialog).toBeTruthy()
|
|
99
|
+
expect(dialog.hasAttribute('aria-modal')).toBe(false)
|
|
100
|
+
expect(b.root.querySelector('.integration-app')!.closest('[inert], [aria-hidden="true"]')).toBeNull()
|
|
101
|
+
expect(getComputedStyle(b.root).pointerEvents).not.toBe('none')
|
|
102
|
+
expect(document.body.getAttribute('style')).toBe(body)
|
|
103
|
+
expect(document.documentElement.getAttribute('style')).toBe(html)
|
|
104
|
+
const before=b.call.mock.calls.length
|
|
105
|
+
click(b.root,'Refresh');await settle()
|
|
106
|
+
expect(b.call.mock.calls.length).toBeGreaterThan(before)
|
|
107
|
+
click(b.root,'Disable');await settle()
|
|
108
|
+
expect(a.root.querySelector('[role="dialog"]')).toBeTruthy()
|
|
109
|
+
expect(b.root.querySelector('[role="dialog"]')).toBeTruthy()
|
|
110
|
+
click(a.root,'Cancel');await settle()
|
|
111
|
+
expect(a.root.querySelector('[role="dialog"]')).toBeNull()
|
|
112
|
+
expect(b.root.querySelector('[role="dialog"]')).toBeTruthy()
|
|
113
|
+
expect(a.root.querySelector('.integration-app')!.closest('[inert], [aria-hidden="true"]')).toBeNull()
|
|
114
|
+
} finally {
|
|
115
|
+
a.app.unmount();b.app.unmount();a.root.remove();b.root.remove();await settle()
|
|
116
|
+
}
|
|
117
|
+
expect(document.body.getAttribute('style')).toBe(body)
|
|
118
|
+
expect(document.documentElement.getAttribute('style')).toBe(html)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('repeated mounts of the same package use unique, package-prefixed ARIA references',async()=>{
|
|
122
|
+
const a=fixture('SamePackage',true),b=fixture('SamePackage',true)
|
|
123
|
+
try {
|
|
124
|
+
await settle();click(a.root,'Disable');click(b.root,'Disable');await settle()
|
|
125
|
+
const ids=[...document.querySelectorAll('[id]')].map(node=>node.id).filter(Boolean)
|
|
126
|
+
expect(new Set(ids).size).toBe(ids.length)
|
|
127
|
+
for(const f of [a,b]) {
|
|
128
|
+
const dialog=f.root.querySelector('[role="dialog"]')!
|
|
129
|
+
for(const attr of ['aria-labelledby','aria-describedby']) {
|
|
130
|
+
const id=dialog.getAttribute(attr)!
|
|
131
|
+
// CLI substitutes the package ID in this template before building.
|
|
132
|
+
expect(id).toContain('__PACKAGE__-')
|
|
133
|
+
expect(f.root.querySelector(`[id="${id}"]`)).toBeTruthy()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} finally {a.app.unmount();b.app.unmount();a.root.remove();b.root.remove();await settle()}
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('unmount releases its sizing class and preserves an existing host class',async()=>{
|
|
140
|
+
const f=fixture('Sizing',false)
|
|
141
|
+
expect(f.root.classList.contains('dsmapp-vue-root')).toBe(true)
|
|
142
|
+
f.app.unmount()
|
|
143
|
+
expect(f.root.classList.contains('dsmapp-vue-root')).toBe(false)
|
|
144
|
+
f.root.classList.add('dsmapp-vue-root')
|
|
145
|
+
const app=mount(f.root,f.ctx);await settle();app.unmount()
|
|
146
|
+
expect(f.root.classList.contains('dsmapp-vue-root')).toBe(true)
|
|
147
|
+
f.root.remove()
|
|
148
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"compilerOptions":{"target":"ES2022","module":"ESNext","moduleResolution":"Bundler","strict":true,"lib":["ES2022","DOM"],"skipLibCheck":true,"types":["vite/client"]},"include":["src/**/*.ts","src/**/*.vue","vite.config.ts"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
plugins: [vue()],
|
|
5
|
+
// Library mode must replace Vue dependency env guards for the DSM browser.
|
|
6
|
+
define: {'process.env.NODE_ENV': JSON.stringify('production')},
|
|
7
|
+
build: {lib: {entry: 'src/main.ts', formats: ['iife'], name: 'DsmVueApp', fileName: () => 'app.js'}, cssCodeSplit: false, rollupOptions: {output: {assetFileNames: 'app.[ext]'}}},
|
|
8
|
+
})
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { type JWK } from "jose";
|
|
2
|
-
export declare const PLATFORM_ATTESTATION_ISSUER = "synopkgland-curated";
|
|
3
|
-
export declare const APP_PACKAGE_ATTESTATION_TYP = "app-package-attestation";
|
|
4
|
-
export declare const APP_PACKAGE_ATTESTATION_VERSION: 2;
|
|
5
|
-
export declare const CURATED_RELEASE_ATTESTATION_TYP = "curated-release-attestation";
|
|
6
|
-
export declare const CURATED_RELEASE_ATTESTATION_VERSION: 1;
|
|
7
|
-
export interface AppPackageSigningKey {
|
|
8
|
-
kid: string;
|
|
9
|
-
privateKey: CryptoKey;
|
|
10
|
-
}
|
|
11
|
-
export interface LabAttestationKeys extends AppPackageSigningKey {
|
|
12
|
-
publicKey: CryptoKey;
|
|
13
|
-
publicJwk: JWK;
|
|
14
|
-
}
|
|
15
|
-
/** Inputs for the token embedded in the generated SPK. */
|
|
16
|
-
export interface CuratedAppPackageAttestationInput {
|
|
17
|
-
packageIdentity: string;
|
|
18
|
-
packageVersion: string;
|
|
19
|
-
packageBuild: number;
|
|
20
|
-
contractDigest: string;
|
|
21
|
-
bundleDigest: string;
|
|
22
|
-
capabilities: string[];
|
|
23
|
-
}
|
|
24
|
-
/** Inputs for the detached token published beside the final SPK. */
|
|
25
|
-
export interface CuratedReleaseAttestationInput {
|
|
26
|
-
packageIdentity: string;
|
|
27
|
-
packageVersion: string;
|
|
28
|
-
packageBuild: number;
|
|
29
|
-
artifactDigest: string;
|
|
30
|
-
embeddedAttestationDigest: string;
|
|
31
|
-
}
|
|
32
|
-
export interface AppPackageAttestationClaims {
|
|
33
|
-
typ: typeof APP_PACKAGE_ATTESTATION_TYP;
|
|
34
|
-
version: typeof APP_PACKAGE_ATTESTATION_VERSION;
|
|
35
|
-
packageIdentity: string;
|
|
36
|
-
packageVersion: string;
|
|
37
|
-
packageBuild: number;
|
|
38
|
-
contractDigest: string;
|
|
39
|
-
bundleDigest: string;
|
|
40
|
-
capabilities: string[];
|
|
41
|
-
issuedAt: string;
|
|
42
|
-
}
|
|
43
|
-
export interface CuratedReleaseAttestationClaims {
|
|
44
|
-
typ: typeof CURATED_RELEASE_ATTESTATION_TYP;
|
|
45
|
-
version: typeof CURATED_RELEASE_ATTESTATION_VERSION;
|
|
46
|
-
packageIdentity: string;
|
|
47
|
-
packageVersion: string;
|
|
48
|
-
packageBuild: number;
|
|
49
|
-
artifactDigest: string;
|
|
50
|
-
embeddedAttestationDigest: string;
|
|
51
|
-
issuedAt: string;
|
|
52
|
-
}
|
|
53
|
-
export declare function loadOrCreateLabAttestationKeys(filePath: string): Promise<LabAttestationKeys>;
|
|
54
|
-
/**
|
|
55
|
-
* Sign the local development token used by the inner-loop flow.
|
|
56
|
-
* Curated releases must use the v2 function below and never this helper.
|
|
57
|
-
*/
|
|
58
|
-
export declare function signAppPackageAttestationToken(input: {
|
|
59
|
-
packageIdentity: string;
|
|
60
|
-
contractDigest: string;
|
|
61
|
-
bundleDigest: string;
|
|
62
|
-
capabilities: string[];
|
|
63
|
-
}, keys: LabAttestationKeys): Promise<{
|
|
64
|
-
token: string;
|
|
65
|
-
attestationDigest: string;
|
|
66
|
-
}>;
|
|
67
|
-
/** Sign the v2 App Package Attestation embedded in a Curated SPK. */
|
|
68
|
-
export declare function signCuratedAppPackageAttestationToken(input: CuratedAppPackageAttestationInput, key: AppPackageSigningKey): Promise<{
|
|
69
|
-
token: string;
|
|
70
|
-
attestationDigest: string;
|
|
71
|
-
claims: AppPackageAttestationClaims;
|
|
72
|
-
}>;
|
|
73
|
-
/** Sign the v1 detached release attestation for a final SPK archive. */
|
|
74
|
-
export declare function signCuratedReleaseAttestationToken(input: CuratedReleaseAttestationInput, key: AppPackageSigningKey): Promise<{
|
|
75
|
-
token: string;
|
|
76
|
-
attestationDigest: string;
|
|
77
|
-
claims: CuratedReleaseAttestationClaims;
|
|
78
|
-
}>;
|
|
79
|
-
/** Stable JSON representation used by the embedded token's jti binding. */
|
|
80
|
-
export declare function canonicalAppPackageClaims(claims: AppPackageAttestationClaims): string;
|
|
81
|
-
export declare function digestAppPackageClaims(claims: AppPackageAttestationClaims): string;
|
|
82
|
-
/** Stable JSON representation used by the detached token's jti binding. */
|
|
83
|
-
export declare function canonicalCuratedReleaseClaims(claims: CuratedReleaseAttestationClaims): string;
|
|
84
|
-
export declare function digestCuratedReleaseClaims(claims: CuratedReleaseAttestationClaims): string;
|
|
85
|
-
export declare function normalizeCapabilities(capabilities: string[]): string[];
|
|
86
|
-
export declare function assertPackageVersionBinding(packageVersion: string, packageBuild: number): void;
|