@adep/cli 0.1.17 → 0.2.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 (29) hide show
  1. package/README.md +96 -12
  2. package/dist/index.js +27103 -10486
  3. package/package.json +8 -6
  4. package/templates/app-desktop/README.md +51 -0
  5. package/templates/app-desktop/main.mjs +108 -0
  6. package/templates/app-desktop/offline-runtime.mjs +129 -0
  7. package/templates/app-desktop/package.json +49 -0
  8. package/templates/app-desktop/preload.cjs +45 -0
  9. package/templates/app-desktop/template.json +14 -0
  10. package/templates/app-desktop/window.mjs +53 -0
  11. package/templates/app-mobile/README.md +89 -0
  12. package/templates/app-mobile/adep.permissions.json +4 -0
  13. package/templates/app-mobile/bridge/gate.mjs +88 -0
  14. package/templates/app-mobile/capacitor.config.ts +23 -0
  15. package/templates/app-mobile/package.json +24 -0
  16. package/templates/app-mobile/plugins/adep-bridge/android/build.gradle +22 -0
  17. package/templates/app-mobile/plugins/adep-bridge/android/src/main/AndroidManifest.xml +9 -0
  18. package/templates/app-mobile/plugins/adep-bridge/android/src/main/java/com/adep/bridge/AdepBridge.kt +86 -0
  19. package/templates/app-mobile/plugins/adep-bridge/android/src/main/java/com/adep/bridge/AdepKeystore.kt +81 -0
  20. package/templates/app-mobile/plugins/adep-bridge/android/src/main/java/com/adep/bridge/AdepPermissionGate.kt +81 -0
  21. package/templates/app-mobile/plugins/adep-bridge/capacitor.plugin.json +10 -0
  22. package/templates/app-mobile/plugins/adep-bridge/dist/plugin.js +44 -0
  23. package/templates/app-mobile/plugins/adep-bridge/ios/AdepBridge.podspec +14 -0
  24. package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepBridge.swift +69 -0
  25. package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepKeychain.swift +58 -0
  26. package/templates/app-mobile/plugins/adep-bridge/ios/Sources/AdepPermissionGate.swift +82 -0
  27. package/templates/app-mobile/plugins/adep-bridge/package.json +14 -0
  28. package/templates/app-mobile/template.json +16 -0
  29. package/templates/app-mobile/web/adep-bridge.js +56 -0
@@ -0,0 +1,82 @@
1
+ import Foundation
2
+
3
+ /// 原生侧权限门控(iOS,APP-009)。
4
+ ///
5
+ /// 与 `bridge/gate.mjs`(host-side harness 镜像)**同规则**:`permissions` 白名单 contains
6
+ /// 判定 + 审计记录。两侧独立实现、共用同一份清单契约(`adep.permissions.json`)与同一
7
+ /// 错误码 `PERMISSION_DENIED`。规则刻意极简(没有第二种判定形态),漂移面最小;
8
+ /// 模板单测钉住「Swift 源码引用同一清单文件名与错误码」。
9
+ public final class AdepPermissionGate {
10
+ /// 与 bridge/gate.mjs 共用的契约常量。
11
+ public static let permissionDenied = "PERMISSION_DENIED"
12
+ /// 与 CLI 写入文件名一致的契约常量(app/site/adep.permissions.json → cap sync → Public/)。
13
+ static let manifestFileName = "adep.permissions.json"
14
+
15
+ public enum Result {
16
+ case granted
17
+ case denied(reason: String)
18
+ }
19
+
20
+ private let permissions: [String]
21
+ private let audit: AdepAuditLog
22
+
23
+ private init(permissions: [String], audit: AdepAuditLog) {
24
+ self.permissions = permissions
25
+ self.audit = audit
26
+ }
27
+
28
+ /// 从 bundle 读权限清单并装配门控。
29
+ /// 清单缺失 → throw(调用方 fail-closed:门控建立不了 = 一切原生能力不可用)。
30
+ static func load(from bundle: Bundle) throws -> AdepPermissionGate {
31
+ guard let url = bundle.url(forResource: manifestFileName, withExtension: nil) else {
32
+ throw NSError(
33
+ domain: "AdepBridge", code: 1,
34
+ userInfo: [NSLocalizedDescriptionKey: "Bundle 缺少 \(manifestFileName)(CLI build 写入、cap sync 同步)"]
35
+ )
36
+ }
37
+ let data = try Data(contentsOf: url)
38
+ let object = try JSONSerialization.jsonObject(with: data) as? [String: Any]
39
+ let raw = object?["permissions"] as? [String] ?? []
40
+ return AdepPermissionGate(permissions: raw, audit: AdepAuditLog.shared)
41
+ }
42
+
43
+ /// 声明面判定 + 审计(尝试即记,无论放行还是拒绝)。
44
+ func check(_ capability: String) -> Result {
45
+ let declared = permissions.contains(capability)
46
+ audit.log(
47
+ ts: ISO8601DateFormatter().string(from: Date()),
48
+ capability: capability,
49
+ result: declared ? "granted" : "denied",
50
+ reason: declared ? "ok" : "not_declared"
51
+ )
52
+ if declared { return .granted }
53
+ return .denied(reason: "原生能力 \"\(capability)\" 未在应用 manifest 的 permissions 中声明(未声明即不可用,且不会在运行时申请)")
54
+ }
55
+ }
56
+
57
+ /// 设备侧审计日志(iOS):沙盒 Documents/adep-audit.log 追加一行。
58
+ /// 与 gate.mjs 的 AuditEntry 形状一致(ts / capability / result / reason)。
59
+ public final class AdepAuditLog {
60
+ static let shared = AdepAuditLog()
61
+
62
+ private let fileURL: URL
63
+
64
+ private init() {
65
+ let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
66
+ fileURL = documents.appendingPathComponent("adep-audit.log")
67
+ }
68
+
69
+ func log(ts: String, capability: String, result: String, reason: String) {
70
+ let line = "{\"ts\":\"\(ts)\",\"capability\":\"\(capability)\",\"result\":\"\(result)\",\"reason\":\"\(reason)\"}\n"
71
+ guard let data = line.data(using: .utf8) else { return }
72
+ if FileManager.default.fileExists(atPath: fileURL.path) {
73
+ if let handle = try? FileHandle(forWritingTo: fileURL) {
74
+ defer { try? handle.close() }
75
+ handle.seekToEndOfFile()
76
+ handle.write(data)
77
+ }
78
+ } else {
79
+ try? data.write(to: fileURL, options: .atomic)
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@adep/bridge",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "AgentDeploy 移动壳原生桥(APP-009):原生侧权限门控 + ownerKey 存储(Keychain / Keystore)+ 原生能力 invoke 路由",
6
+ "main": "dist/plugin.js",
7
+ "types": "dist/plugin.d.ts",
8
+ "scripts": {
9
+ "verify": "node -e \"require('./dist/plugin.js')\""
10
+ },
11
+ "peerDependencies": {
12
+ "@capacitor/core": "^7.0.0"
13
+ }
14
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "code": "app-mobile",
3
+ "name": "移动应用壳(Capacitor)",
4
+ "description": "Capacitor 移动壳工程:WebView 消费 app/site/ 产物,在线运行时(云函数经 platformUrl 服务端执行);原生桥(plugins/adep-bridge)承载原生侧权限门控与 ownerKey 存储(Keychain / Keystore)",
5
+ "version": "1.0.0",
6
+ "variables": [
7
+ {
8
+ "name": "projectSlug",
9
+ "description": "平台项目 slug(写入包名 / adep.app.json / bundle id 推导)"
10
+ },
11
+ { "name": "appName", "description": "应用显示名(iOS / Android 显示名)" },
12
+ { "name": "platformUrl", "description": "平台地址(页面侧 @adep/client 以此为 baseUrl invoke 云函数)" },
13
+ { "name": "iosBundleId", "description": "iOS bundle identifier(top.adep.<slug>,连字符合法)" },
14
+ { "name": "androidPackageId", "description": "Android applicationId(top.adep.<slug 连字符转下划线>,applicationId 不允许连字符)" }
15
+ ]
16
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 页面侧桥(APP-009 移动壳,与桌面壳 preload.cjs **同形**——§3.2.8 硬约束④:移动与桌面桥
3
+ * 同形,同一套页面代码不区分运行壳)。
4
+ *
5
+ * CLI `adep app build --platform ios,android` 时把本文件复制为 `app/site/adep-bridge.js`
6
+ * 并在 `index.html` 注入 `<script src="/adep-bridge.js"></script>`(幂等),同时把
7
+ * `adep.app.json` 复制到 `app/site/adep.app.json`。页面加载时本脚本异步读取配置并暴露
8
+ * `window.__ADEP_APP__`——与桌面 preload 暴露的字段一一对应。
9
+ *
10
+ * 本文件**自包含**(不 import 任何 npm 包):WebView 里 `@capacitor/core` 由页面 bundle
11
+ * 提供(`window.Capacitor` 运行时全局),壳不能假设 site 产物打包过它。
12
+ *
13
+ * 页面侧调用云函数(在线运行时)与桌面同款:
14
+ *
15
+ * const client = createAdepClient({ baseUrl: window.__ADEP_APP__.platformUrl })
16
+ * const result = await client.invokeFunction('hello', { name: 'world' })
17
+ *
18
+ * 设备端函数(离线运行时)请求原生能力:
19
+ *
20
+ * const result = await window.__ADEP_APP__.invokeNative('geolocation', {})
21
+ * // 未在 manifest permissions 声明 → 原生侧拒绝 { code: 'PERMISSION_DENIED' }
22
+ */
23
+ ;(async () => {
24
+ /** 壳配置(CLI build 时从壳工程 adep.app.json 复制进 site/;字段校验归 CLI schema)。 */
25
+ const response = await fetch('./adep.app.json')
26
+ const config = await response.json()
27
+
28
+ // 字符串 key 写法(与桌面 preload.cjs 同款):`__ADEP_APP__` 是壳与页面约定的全局名,
29
+ // 以标识符出现会触发 no-underscore-dangle(lint),字符串形式表达「这是契约名不是变量」。
30
+ window['__ADEP_APP__'] = {
31
+ platformUrl: config.platformUrl,
32
+ projectSlug: config.projectSlug,
33
+ appName: config.appName || config.projectSlug,
34
+ // 本模板交付在线运行时(PRD §2.11 路径 A:函数在服务端执行,改逻辑不需要重新发版)。
35
+ // 移动离线运行时(设备端执行 + 原生能力)是后续单;桥的 invokeNative 入口已就位,
36
+ // 离线注入接上后页面调用形态不变。
37
+ mode: 'online',
38
+ /**
39
+ * 原生能力入口:经 Capacitor 插件 `@adep/bridge`(plugins/adep-bridge)→ 原生侧门控。
40
+ * 门控做在原生侧:未声明权限即不可用(PERMISSION_DENIED)且不在运行时申请。
41
+ */
42
+ invokeNative: (capability, args) => {
43
+ const plugin =
44
+ window.Capacitor &&
45
+ window.Capacitor.Plugins &&
46
+ window.Capacitor.Plugins.AdepBridge
47
+ if (!plugin) {
48
+ return Promise.reject({
49
+ code: 'NATIVE_BRIDGE_UNAVAILABLE',
50
+ message: '原生桥不可用:页面未运行在 AdepBridge 注入的移动壳内',
51
+ })
52
+ }
53
+ return plugin.invokeNative({ capability, args })
54
+ },
55
+ }
56
+ })()