@agimon-ai/po-native 0.0.0-stage → 0.0.1-alpha.4
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/LICENSE +149 -0
- package/README.md +161 -2
- package/app.config.ts +84 -0
- package/assets/icon.png +0 -0
- package/assets/welcome-artwork.png +0 -0
- package/assets/welcome-logo-dark.png +0 -0
- package/assets/welcome-logo-light.png +0 -0
- package/babel.config.cjs +8 -0
- package/generated/richAssets.json +1 -0
- package/metro.config.cjs +48 -0
- package/modules/po-device-key/android/build.gradle +19 -0
- package/modules/po-device-key/android/src/main/java/expo/modules/podevicekey/PoDeviceKeyModule.kt +174 -0
- package/modules/po-device-key/expo-module.config.json +5 -0
- package/modules/po-device-key/index.ts +72 -0
- package/modules/po-device-key/ios/PoDeviceKey.podspec +17 -0
- package/modules/po-device-key/ios/PoDeviceKeyModule.swift +134 -0
- package/modules/po-webrtc-speaker/expo-module.config.json +6 -0
- package/modules/po-webrtc-speaker/ios/PoWebRtcSpeaker.podspec +24 -0
- package/modules/po-webrtc-speaker/ios/PoWebRtcSpeakerAppDelegate.swift +20 -0
- package/package.json +129 -3
- package/src/adapters/abortSignal.ts +42 -0
- package/src/adapters/crypto.ts +31 -0
- package/src/adapters/remoteHttp.ts +153 -0
- package/src/adapters/remoteSocket.ts +176 -0
- package/src/app/AccountRoute.tsx +706 -0
- package/src/app/App.tsx +3121 -0
- package/src/app/GrantNotices.tsx +37 -0
- package/src/app/HostsRoutes.tsx +506 -0
- package/src/app/appearance.ts +40 -0
- package/src/app/cloudRuntime.ts +390 -0
- package/src/app/firstSteps.ts +28 -0
- package/src/app/homeState.ts +99 -0
- package/src/app/hostRuntimes.ts +398 -0
- package/src/app/liveRuntime.ts +691 -0
- package/src/app/nativeTemplate.tsx +142 -0
- package/src/app/otherWorkspaces.ts +27 -0
- package/src/app/railWorkspaces.ts +67 -0
- package/src/app/sessionView.ts +27 -0
- package/src/app/useHome.ts +233 -0
- package/src/components/ActivityPanel.tsx +49 -0
- package/src/components/ActivitySheet.stories.tsx +64 -0
- package/src/components/ActivitySheet.tsx +311 -0
- package/src/components/CaptureTray.tsx +93 -0
- package/src/components/CompletionSheet.stories.tsx +126 -0
- package/src/components/CompletionSheet.tsx +378 -0
- package/src/components/Composer.tsx +453 -0
- package/src/components/ConnectCard.tsx +171 -0
- package/src/components/ContextFace.stories.tsx +94 -0
- package/src/components/ContextFace.tsx +426 -0
- package/src/components/DashboardTiles.tsx +148 -0
- package/src/components/DialogSheet.stories.tsx +100 -0
- package/src/components/DialogSheet.tsx +114 -0
- package/src/components/GrantNotice.tsx +54 -0
- package/src/components/MessageActionsSheet.stories.tsx +45 -0
- package/src/components/MessageActionsSheet.tsx +37 -0
- package/src/components/RestoreCard.tsx +73 -0
- package/src/components/ResumeSheetView.tsx +125 -0
- package/src/components/SessionChoicesView.tsx +218 -0
- package/src/components/SessionOptionsSheet.tsx +429 -0
- package/src/components/SessionSheets.tsx +152 -0
- package/src/components/SocialSignInButton.stories.tsx +25 -0
- package/src/components/SocialSignInButton.tsx +30 -0
- package/src/components/TabBar.stories.tsx +48 -0
- package/src/components/TabBar.tsx +117 -0
- package/src/components/TimelineItem.tsx +238 -0
- package/src/components/contextRows.ts +48 -0
- package/src/components/homeApps.ts +69 -0
- package/src/components/richActivity/AuthorMediaHost.tsx +405 -0
- package/src/components/richActivity/ImageViewer.stories.tsx +49 -0
- package/src/components/richActivity/ImageViewer.tsx +92 -0
- package/src/components/richActivity/McpAppChrome.stories.tsx +62 -0
- package/src/components/richActivity/McpAppChrome.tsx +54 -0
- package/src/components/richActivity/McpAppView.tsx +289 -0
- package/src/components/richActivity/NativeAuthorCaptureSurface.tsx +180 -0
- package/src/components/richActivity/NativeAuthorHtmlView.tsx +100 -0
- package/src/components/richActivity/RichActivityPreview.stories.tsx +115 -0
- package/src/components/richActivity/authorHtmlPolicy.ts +70 -0
- package/src/components/richActivity/authorMediaFiles.ts +133 -0
- package/src/components/richActivity/authorMediaValidation.ts +111 -0
- package/src/components/richActivity/createRichActivityRenderers.tsx +486 -0
- package/src/components/richActivity/isolatedContent.ts +36 -0
- package/src/components/richActivity/mcpAppBridge.ts +165 -0
- package/src/components/richActivity/offlineDocuments.ts +54 -0
- package/src/components/richActivity/terminalFollow.ts +110 -0
- package/src/components/slotProps.ts +159 -0
- package/src/components/tabHeader.ts +5 -0
- package/src/index.tsx +50 -0
- package/src/screens/AccountOnboarding.stories.tsx +121 -0
- package/src/screens/AccountScreen.tsx +700 -0
- package/src/screens/ActionFlowDesign.stories.tsx +476 -0
- package/src/screens/ActivityDesign.stories.tsx +361 -0
- package/src/screens/AppearanceScreen.tsx +240 -0
- package/src/screens/ClientDevices.stories.tsx +56 -0
- package/src/screens/ClientDevices.tsx +119 -0
- package/src/screens/ClientDevicesView.tsx +127 -0
- package/src/screens/ComposerOptionsDesign.stories.tsx +363 -0
- package/src/screens/ComputerDetail.stories.tsx +48 -0
- package/src/screens/ContributedSettingsScreen.tsx +418 -0
- package/src/screens/ConversationDesign.stories.tsx +446 -0
- package/src/screens/GitRemoteScreen.tsx +207 -0
- package/src/screens/Grants.stories.tsx +47 -0
- package/src/screens/HomeComputers.stories.tsx +84 -0
- package/src/screens/HomeConnectDesign.stories.tsx +233 -0
- package/src/screens/HomeScreen.stories.tsx +542 -0
- package/src/screens/HomeScreen.tsx +678 -0
- package/src/screens/HostDrawer.stories.tsx +35 -0
- package/src/screens/HostDrawer.tsx +284 -0
- package/src/screens/HostsView.tsx +391 -0
- package/src/screens/ImagesScreen.tsx +115 -0
- package/src/screens/LargeContentDesign.stories.tsx +481 -0
- package/src/screens/McpAddServerDesign.stories.tsx +72 -0
- package/src/screens/McpServersScreen.tsx +428 -0
- package/src/screens/MetricsScreen.tsx +450 -0
- package/src/screens/MultiHostDesign.stories.tsx +260 -0
- package/src/screens/NativeListsDesign.stories.tsx +734 -0
- package/src/screens/PairingScreen.stories.tsx +34 -0
- package/src/screens/PairingScreen.tsx +113 -0
- package/src/screens/PluginActionsDesign.stories.tsx +107 -0
- package/src/screens/PluginsScreen.tsx +97 -0
- package/src/screens/ProvidersScreen.stories.tsx +40 -0
- package/src/screens/ProvidersScreen.tsx +643 -0
- package/src/screens/RemoteControlScreen.tsx +226 -0
- package/src/screens/SessionActionsDesign.stories.tsx +475 -0
- package/src/screens/SessionListScreen.stories.tsx +32 -0
- package/src/screens/SessionListScreen.tsx +137 -0
- package/src/screens/SessionScreen.stories.tsx +231 -0
- package/src/screens/SessionScreen.tsx +666 -0
- package/src/screens/SettingsComputers.stories.tsx +57 -0
- package/src/screens/SettingsDesign.stories.tsx +219 -0
- package/src/screens/SettingsFields.tsx +332 -0
- package/src/screens/SettingsParityDesign.stories.tsx +508 -0
- package/src/screens/SettingsRound2Design.stories.tsx +1092 -0
- package/src/screens/SettingsScreen.stories.tsx +321 -0
- package/src/screens/SettingsScreen.tsx +424 -0
- package/src/screens/WorkspaceProviders.stories.tsx +109 -0
- package/src/screens/WorkspaceProviders.tsx +201 -0
- package/src/screens/WorkspaceProvidersSection.tsx +226 -0
- package/src/screens/WorkspaceScreen.stories.tsx +163 -0
- package/src/screens/WorkspaceScreen.tsx +46 -0
- package/src/screens/WorkspaceSettingsScreen.tsx +614 -0
- package/src/screens/accountBack.ts +15 -0
- package/src/screens/designCaptures.ts +26 -0
- package/src/screens/designFrame.tsx +32 -0
- package/src/screens/homeModel.ts +309 -0
- package/src/screens/workspaceModel.ts +23 -0
- package/src/services/accountClient/index.ts +292 -0
- package/src/services/accountGrants/index.ts +147 -0
- package/src/services/accountRoster/index.ts +390 -0
- package/src/services/accountTrust/index.ts +261 -0
- package/src/services/apiTransport/index.ts +202 -0
- package/src/services/bundleCache/index.ts +240 -0
- package/src/services/byteCodec/index.ts +10 -0
- package/src/services/calendarTargets/index.ts +37 -0
- package/src/services/clientIdentity/index.ts +77 -0
- package/src/services/cloudHosts/index.ts +166 -0
- package/src/services/compositionLoader/index.ts +21 -0
- package/src/services/connection/index.ts +107 -0
- package/src/services/deviceTrust/index.ts +767 -0
- package/src/services/hostActions/index.ts +381 -0
- package/src/services/hostLinkBroker/index.ts +208 -0
- package/src/services/hostModules/index.ts +87 -0
- package/src/services/hostSnapshots/index.ts +210 -0
- package/src/services/issuerSelection.native.ts +27 -0
- package/src/services/legacyAppBundle/index.ts +11 -0
- package/src/services/logExport/index.ts +185 -0
- package/src/services/nativeAudio/index.ts +295 -0
- package/src/services/nativeAuthorDrafts/index.ts +191 -0
- package/src/services/nativePush/index.ts +201 -0
- package/src/services/nativeRealtime/index.ts +120 -0
- package/src/services/nativeVoice/index.ts +82 -0
- package/src/services/pairing/index.ts +235 -0
- package/src/services/pluginRuntime/index.ts +353 -0
- package/src/services/runtimeProof/index.ts +75 -0
- package/src/services/sessionProfiles/index.ts +98 -0
- package/src/services/sessionProfiles/type.ts +14 -0
- package/src/services/sessionRuntime/index.ts +214 -0
- package/src/services/settingsApi/index.ts +560 -0
- package/src/services/workspaceProviders/index.ts +153 -0
- package/src/types/assets.d.ts +4 -0
- package/src/types/poTemplateAdvanced.d.ts +9 -0
- package/src/unistyles.ts +18 -0
- package/styles/preview.css +10 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
License text copyright © 2017 MariaDB Corporation Ab, All Rights Reserved.
|
|
2
|
+
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
|
3
|
+
|
|
4
|
+
Parameters
|
|
5
|
+
|
|
6
|
+
Licensor: Agimon Pty Ltd
|
|
7
|
+
Licensed Work: Po, the software in the Po source repository and the
|
|
8
|
+
packages released from it, in each version released by
|
|
9
|
+
the Licensor.
|
|
10
|
+
The Licensed Work is (c) 2026 Agimon Pty Ltd.
|
|
11
|
+
Material identified as third-party in the "Third-party
|
|
12
|
+
components" section at the end of this file or in a
|
|
13
|
+
NOTICE file, and any package or directory whose own
|
|
14
|
+
license file grants a different license, is not part of
|
|
15
|
+
the Licensed Work and remains under its own license.
|
|
16
|
+
Additional Use Grant: You may make production use of the Licensed Work if
|
|
17
|
+
your use is Eligible Use. "Eligible Use" is production use that falls
|
|
18
|
+
within at least one of the following:
|
|
19
|
+
|
|
20
|
+
(a) Personal Use. Use by an individual for their own personal purposes,
|
|
21
|
+
and not on behalf of, or for the benefit of, an employer, client, or
|
|
22
|
+
other Organization. Use by an individual in the course of employment
|
|
23
|
+
or engagement by an Organization is use by that Organization.
|
|
24
|
+
|
|
25
|
+
(b) Small Organization Use. Use by or on behalf of an Organization whose
|
|
26
|
+
Annual Revenue is less than USD 1,000,000.
|
|
27
|
+
|
|
28
|
+
(c) Non-profit, Education and Research Use. Use, regardless of Annual
|
|
29
|
+
Revenue, by (i) an Organization registered or recognized as a
|
|
30
|
+
non-profit or charitable organization under the laws of its
|
|
31
|
+
jurisdiction, for its non-profit purposes; (ii) an accredited school,
|
|
32
|
+
college, or university, for teaching, learning, or academic research;
|
|
33
|
+
or (iii) a publicly funded research institution, for its research.
|
|
34
|
+
|
|
35
|
+
(d) Service Provider Use. Use by a consultant, contractor, agency, or other
|
|
36
|
+
service provider, regardless of its own Annual Revenue, solely to
|
|
37
|
+
build, deploy, host, operate, or support the Licensed Work for a client
|
|
38
|
+
whose own use would be Eligible Use under (a), (b), or (c). A service
|
|
39
|
+
provider's use for its own purposes is Eligible Use only if the service
|
|
40
|
+
provider itself qualifies under (b) or (c).
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Hosted services. Offering the Licensed Work to third parties as a hosted
|
|
44
|
+
or managed service whose primary value is the Licensed Work is Eligible
|
|
45
|
+
Use only if each such third party's own use would be Eligible Use.
|
|
46
|
+
|
|
47
|
+
Definitions. "Organization" means any company, partnership, sole
|
|
48
|
+
proprietorship, trust, association, government body, or other entity, and
|
|
49
|
+
includes an individual acting in a business or professional capacity.
|
|
50
|
+
"Affiliate" means any entity that directly or indirectly controls, is
|
|
51
|
+
controlled by, or is under common control with an Organization, where
|
|
52
|
+
"control" means ownership of more than 50% of the voting interests of an
|
|
53
|
+
entity or the power to direct its management and policies by contract or
|
|
54
|
+
otherwise. "Annual Revenue" means the total gross revenue of an
|
|
55
|
+
Organization and all of its Affiliates combined, from all sources and
|
|
56
|
+
whether or not related to the Licensed Work, for its most recently
|
|
57
|
+
completed fiscal year, or, if it has not completed a fiscal year, for the
|
|
58
|
+
twelve months before the use (or since its formation, if shorter),
|
|
59
|
+
converted into US dollars at the average exchange rate for that period.
|
|
60
|
+
Proceeds of equity or debt financing are not revenue.
|
|
61
|
+
|
|
62
|
+
Threshold. If an Organization stops qualifying under (b), its existing
|
|
63
|
+
production use remains Eligible Use for 90 days after it stops
|
|
64
|
+
qualifying, after which it must obtain a commercial license or stop
|
|
65
|
+
production use. Use that was Eligible Use when it occurred never requires
|
|
66
|
+
a commercial license after the fact.
|
|
67
|
+
Change Date: Four years from the date a version of the Licensed Work
|
|
68
|
+
is first publicly released by the Licensor.
|
|
69
|
+
Change License: Apache License, Version 2.0
|
|
70
|
+
|
|
71
|
+
For information about alternative licensing arrangements for the Licensed
|
|
72
|
+
Work, please visit: https://agimon.ai/licensing
|
|
73
|
+
|
|
74
|
+
Notice
|
|
75
|
+
|
|
76
|
+
The Business Source License (this document, or the "License") is not an Open
|
|
77
|
+
Source license. However, the Licensed Work will eventually be made available
|
|
78
|
+
under an Open Source License, as stated in this License.
|
|
79
|
+
|
|
80
|
+
-----------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
Business Source License 1.1
|
|
83
|
+
|
|
84
|
+
Terms
|
|
85
|
+
|
|
86
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
87
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
88
|
+
Licensor may make an Additional Use Grant, above, permitting limited
|
|
89
|
+
production use.
|
|
90
|
+
|
|
91
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
92
|
+
available distribution of a specific version of the Licensed Work under this
|
|
93
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
94
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
95
|
+
above terminate.
|
|
96
|
+
|
|
97
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
98
|
+
currently in effect as described in this License, you must purchase a
|
|
99
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
100
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
101
|
+
|
|
102
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
103
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
104
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
105
|
+
for each version of the Licensed Work released by Licensor.
|
|
106
|
+
|
|
107
|
+
You must conspicuously display this License on each original or modified copy
|
|
108
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
109
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
110
|
+
License apply to your use of that work.
|
|
111
|
+
|
|
112
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
113
|
+
terminate your rights under this License for the current and all other
|
|
114
|
+
versions of the Licensed Work.
|
|
115
|
+
|
|
116
|
+
This License does not grant you any right in any trademark or logo of
|
|
117
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
118
|
+
Licensor as expressly required by this License).
|
|
119
|
+
|
|
120
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
121
|
+
AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
122
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
123
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
124
|
+
TITLE.
|
|
125
|
+
|
|
126
|
+
MariaDB hereby grants you permission to use this License’s text to license
|
|
127
|
+
your works, and to refer to it using the trademark “Business Source License”,
|
|
128
|
+
as long as you comply with the Covenants of Licensor below.
|
|
129
|
+
|
|
130
|
+
Covenants of Licensor
|
|
131
|
+
|
|
132
|
+
In consideration of the right to use this License’s text and the “Business
|
|
133
|
+
Source License” name and trademark, Licensor covenants to MariaDB, and to all
|
|
134
|
+
other recipients of the licensed work to be provided by Licensor:
|
|
135
|
+
|
|
136
|
+
1. To specify as the Change License the GPL Version 2.0 or any later version,
|
|
137
|
+
or a license that is compatible with GPL Version 2.0 or a later version,
|
|
138
|
+
where “compatible” means that software provided under the Change License can
|
|
139
|
+
be included in a program with software provided under GPL Version 2.0 or a
|
|
140
|
+
later version. Licensor may specify additional Change Licenses without
|
|
141
|
+
limitation.
|
|
142
|
+
|
|
143
|
+
2. To either: (a) specify an additional grant of rights to use that does not
|
|
144
|
+
impose any additional restriction on the right granted in this License, as
|
|
145
|
+
the Additional Use Grant; or (b) insert the text “None”.
|
|
146
|
+
|
|
147
|
+
3. To specify a Change Date.
|
|
148
|
+
|
|
149
|
+
4. Not to modify this License in any other way.
|
package/README.md
CHANGED
|
@@ -1,3 +1,162 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Po Native
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Po phone app for iOS and Android, built with Expo 57 and React Native 0.86. App name "Po", company iOS bundle id `ai.po.app`, Android package `ai.agimon.po`. It is private, experimental (`stage:experimental`), and built locally only: no EAS or other cloud build.
|
|
4
|
+
|
|
5
|
+
The app pairs with one Po host over its remote-access tunnel, lists that host's sessions, shows a session transcript with a composer, and runs the host's native plugin contributions (for example po-task's task tool renderer and task graph channel) from a signed composition it downloads.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
- **Pairing.** Scan the host's pairing QR with the camera, or paste the URL. The host approves the request on its own screen. The app then registers device keys (see the trust model) and keeps the host record.
|
|
10
|
+
- **Connection.** Remote HTTP with the device cookie, three sealed channels (session, protocol, HTTP), the Pi `Client` over the protocol socket, and the hub socket. A dropped hub signs in again with the device key and rebuilds the connection.
|
|
11
|
+
- **Session state.** `@agimon-ai/po-web-dashboard/core` supplies the session, transcript, and plugin registry stores, the same ones the browser cockpit uses, without any DOM dependency.
|
|
12
|
+
- **Plugins.** The host advertises native compositions through `nativeGlobal` and `workspaces[].nativeComposition` on `/api/compositions` and `nativeComposition` on a session summary. For each mount the app loads the composition through its verified cache, evaluates it, keeps only the plugin ids the descriptor admits, installs them in the plugin registry, and starts them. Channel frames reach plugins through `dispatchChannelFrame`. A newer load for a mount makes older ones stale.
|
|
13
|
+
- **Navigation.** `App` registers screens by phase (reconnecting, pairing, ready) in a React Navigation native stack, inside `SafeAreaProvider > PoNativeProvider > KeyboardProvider > NavigationContainer`. Native headers own back navigation and swipe back; Sessions has a large title and a Settings action. A session opens in the live runtime on focus and closes on blur; Settings reloads trusted devices on focus.
|
|
14
|
+
- **Lists and content.** Sessions and transcripts use the component library's FlashList-backed `List`; the transcript uses `startAtBottom`. Assistant text uses `Markdown`; tool entries use the plugin `sessionToolRenderer`, falling back to `MessageItem` and `CodeBlock`.
|
|
15
|
+
- **Sheets.** `PoNativeProvider` supplies the shared gesture root and bottom sheet host for plugin detail sheets and action menus. The composer's decision prompts stay inline as gentle rose panels, not modal sheets. `SettingsScreen` lists trusted devices, revokes one through biometric step-up, and forgets the host.
|
|
16
|
+
- **Styling.** `react-native-unistyles` 3.5.2 with Po's `light` and `dark` themes from `@agimon-ai/po-web-components/nativeTheme`, registered in `src/unistyles.ts` with adaptive themes. Every screen and component keeps its `StyleSheet.create` in its own file so the Unistyles Babel plugin rewrites it.
|
|
17
|
+
|
|
18
|
+
## Trust model
|
|
19
|
+
|
|
20
|
+
The full model is in [Security model](../../../docs/securities.md#trusted-native-devices). In short:
|
|
21
|
+
|
|
22
|
+
- **Pairing needs host approval and biometrics.** If the phone has no Face ID, Touch ID, or fingerprint enrolled, pairing stops with "Set up Face ID or a fingerprint to pair this phone."
|
|
23
|
+
- **Two device keys.** After approval the app generates two P-256 keys and registers them through the sealed gateway: an identity key for silent sign-in, and a step-up key that only biometrics unlock. They live in `expo-secure-store` as `po.device.identity.v1` and `po.device.stepup.v1`; the host record (origin, trusted device id, pinned signer key and revision, label) is `po.host.v1`. All three are `WHEN_UNLOCKED_THIS_DEVICE_ONLY`.
|
|
24
|
+
- **Reconnect.** On start the app signs in with the identity key, no QR and no host approval. It trusts the host's fresh tunnel key only after the bundle signer it pinned from the QR attests that key over the app's own nonce. A changed signer fails closed and Home shows the connect card again.
|
|
25
|
+
- **Step-up.** Every gated action from a device-key session needs a biometric signature, on every tunnel. `fetchWithStepUp` retries a request once after a 401 that names an action.
|
|
26
|
+
- **Revocation.** From the host UI, or from a paired phone after step-up (`device.revoke`). A quick-tunnel browser cannot revoke. Forget host requires successful remote revocation before deleting local keys; failure retains them for retry. A confirmed revocation is checkpointed so interrupted local cleanup can finish after restart. A biometric change invalidates the step-up key; pair again by QR.
|
|
27
|
+
- **Keys are software keys.** They are decrypted into JS memory to sign. They are not Secure Enclave or StrongBox keys.
|
|
28
|
+
|
|
29
|
+
### Trusted compositions are not sandboxed
|
|
30
|
+
|
|
31
|
+
A native composition is host code, one CommonJS module per plugin. The app verifies each module before running it: on every load, cached or downloaded, it checks the signed manifest against the pinned signer with the `native` bundle profile (top-level `.js` modules, each at most 4 MiB), requires the exact advertised revision, and checks that module's hash. The same revision with a different hash is refused, a signer change clears the cache, and the cache keeps at most 64 modules under `Paths.cache/po/compositions/`. It then evaluates each module with `new Function` against a frozen map of host modules (`NATIVE_HOST_MODULE_IDS` from `@agimon-ai/po-core/native`); any other `require` throws. A module that fails to load, verify, or evaluate is left out with a warning and the rest still run. That map is a compatibility boundary, not a sandbox: a verified module runs with the app's full JavaScript privileges.
|
|
32
|
+
|
|
33
|
+
## Local checks
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
pnpm install
|
|
37
|
+
pnpm nx run-many -t lint typecheck build test -p @agimon-ai/po-native --parallel=2
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`build` runs the proof fixture builder and `expo export --platform all` into `dist/shell`. `build:proof` (`scripts/buildProof.mjs`) builds a small counter composition and signs it with the `native` profile; tests use it as a fixture. Node tests do not exercise native crypto, Hermes, or native modules.
|
|
41
|
+
|
|
42
|
+
## Running on a device
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
pnpm nx run @agimon-ai/po-native:prebuild
|
|
46
|
+
pnpm --filter @agimon-ai/po-native ios:setup
|
|
47
|
+
pnpm nx run @agimon-ai/po-native:ios
|
|
48
|
+
JAVA_HOME=$(/usr/libexec/java_home -v 17) pnpm nx run @agimon-ai/po-native:android
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`ios` and `android` are local Release builds. `ios:setup` installs CocoaPods 1.16.2 under `.build/gems` and the iOS commands set `GEM_HOME`, `GEM_PATH`, and `PATH` for that process only; system gems and shell configuration are untouched. The generated `ios/` and `android/` folders, Expo state, and `.build/` are gitignored and never edited by hand; signing changes come from `@agimon-ai/po-expo-signing` during prebuild.
|
|
52
|
+
|
|
53
|
+
The company iOS build requires an `ai.po.app` provisioning profile with Push Notifications. Previous
|
|
54
|
+
`ai.agimon.po` iOS installs are a separate app identity; rebuild and pair the company app. Native push
|
|
55
|
+
checks the installed iOS application ID before enrollment and reads its provisioning-profile APNs
|
|
56
|
+
environment, with a production fallback for App Store/TestFlight installs.
|
|
57
|
+
Android remains `ai.agimon.po` with FCM. See [push setup](../../foundations/po-notification/README.md#native-push-server-credentials).
|
|
58
|
+
|
|
59
|
+
## Ad hoc builds
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
pnpm nx run @agimon-ai/po-native:ios:adhoc
|
|
63
|
+
pnpm nx run @agimon-ai/po-native:android:adhoc
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Both run `scripts/buildAdHoc.mjs`, which fails before touching earlier outputs when a required variable is missing. Every command runs through `spawnSync` with an argument array, and secret values are redacted from console output and logs.
|
|
67
|
+
|
|
68
|
+
- **iOS** (macOS with Xcode): build the proof fixture, `expo prebuild --clean --platform ios`, `pod install`, `xcodebuild archive`, then `-exportArchive` with a temporary `ExportOptions.plist` (`method: release-testing`, manual signing) that is deleted afterwards. Run `ios:setup` first.
|
|
69
|
+
- **Android**: build the proof fixture, `expo prebuild --clean --platform android`, then `./gradlew assembleRelease` under JDK 17.
|
|
70
|
+
|
|
71
|
+
| Variable | Platform | Required | Meaning |
|
|
72
|
+
| ------------------------------------- | -------- | -------- | ------------------------------------------------------------------------------- |
|
|
73
|
+
| `APPLE_TEAM_ID` | iOS | yes | Team id for `appleTeamId`, `DEVELOPMENT_TEAM`, and the export plist |
|
|
74
|
+
| `PO_NATIVE_IOS_PROFILE` | iOS | yes | Provisioning profile name; the Release target switches to manual signing |
|
|
75
|
+
| `PO_NATIVE_IOS_CODE_SIGN_IDENTITY` | iOS | no | `CODE_SIGN_IDENTITY` and export `signingCertificate`, e.g. `Apple Distribution` |
|
|
76
|
+
| `PO_NATIVE_ANDROID_KEYSTORE` | Android | yes | Path to the release keystore, made absolute before Gradle runs |
|
|
77
|
+
| `PO_NATIVE_ANDROID_KEYSTORE_PASSWORD` | Android | yes | Keystore password |
|
|
78
|
+
| `PO_NATIVE_ANDROID_KEY_ALIAS` | Android | yes | Key alias |
|
|
79
|
+
| `PO_NATIVE_ANDROID_KEY_PASSWORD` | Android | yes | Key password |
|
|
80
|
+
| `JAVA_HOME` | Android | no | JDK 17; on macOS it defaults to `/usr/libexec/java_home -v 17` |
|
|
81
|
+
|
|
82
|
+
Secrets come only from these variables or the keychain. Gradle reads the Android values at build time, so none is written into the generated project.
|
|
83
|
+
|
|
84
|
+
**Output.** `.build/adhoc/po-native.ipa` or `.build/adhoc/po-native.apk`, plus `.build/adhoc/SHA256SUMS` covering whichever artifacts exist. Per-step logs (mode `0600`) go to `.build/adhoc/logs/<platform>/`, and intermediate files to `.build/adhoc/work/<platform>/`.
|
|
85
|
+
|
|
86
|
+
## TestFlight archive and export
|
|
87
|
+
|
|
88
|
+
Use an installed **App Store Connect distribution profile** for `ai.po.app`, with Push Notifications,
|
|
89
|
+
and the matching **Apple Distribution certificate and private key** in the local keychain. A development
|
|
90
|
+
or ad hoc profile is not interchangeable. Run `ios:setup` first. No App Store Connect API key is needed
|
|
91
|
+
for this local export.
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
export APPLE_TEAM_ID="RYQ366PW4R"
|
|
95
|
+
export PO_NATIVE_IOS_PROFILE="YOUR_APP_STORE_CONNECT_PROFILE"
|
|
96
|
+
export PO_NATIVE_IOS_CODE_SIGN_IDENTITY="Apple Distribution"
|
|
97
|
+
export PO_NATIVE_IOS_BUILD_NUMBER="42"
|
|
98
|
+
export PO_NATIVE_APNS_ENVIRONMENT="production"
|
|
99
|
+
pnpm nx run @agimon-ai/po-native:ios:testflight
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The same command is available as `pnpm --filter @agimon-ai/po-native ios:testflight`. It reuses the
|
|
103
|
+
local iOS archive script, defaults the signing identity to `Apple Distribution`, and supplies production
|
|
104
|
+
APNs to prebuild. An explicitly configured sandbox environment is rejected before building.
|
|
105
|
+
`PO_NATIVE_IOS_BUILD_NUMBER` is required, becomes Expo's `ios.buildNumber`, and must increase for each
|
|
106
|
+
submitted build. Use a valid `CFBundleVersion`, for example `42` or `1.2.3` (first component 1-9999,
|
|
107
|
+
remaining components 0-99). The marketing version remains `version` in `app.config.ts`.
|
|
108
|
+
|
|
109
|
+
Before export, the script verifies the archive's bundle ID, build number and signed production APNs
|
|
110
|
+
entitlement. It exports with `method: app-store-connect`, `destination: export`, manual signing, and
|
|
111
|
+
`manageAppVersionAndBuildNumber: false`. It does not upload, submit, install, create credentials, or
|
|
112
|
+
modify the Apple portal. Upload remains a separate, explicitly approved action.
|
|
113
|
+
|
|
114
|
+
TestFlight installs have no embedded provisioning profile. When the profile environment is unavailable,
|
|
115
|
+
the app checks the native release type and registers `production` only for App Store/TestFlight builds.
|
|
116
|
+
Other release types still require an explicit profile environment. After pairing and allowing notifications,
|
|
117
|
+
open the app and verify `nativePush.environment: "production"` on its trusted device record in the
|
|
118
|
+
server's `~/.po/web/trusted-devices.json`. Do not print or share the token. This confirms enrollment,
|
|
119
|
+
not APNs delivery.
|
|
120
|
+
|
|
121
|
+
**Output:** `.build/testflight/po-native.ipa` and `.build/testflight/SHA256SUMS`. The archive and dSYMs
|
|
122
|
+
remain under `.build/testflight/work/ios/`, with private per-step logs under `.build/testflight/logs/ios/`.
|
|
123
|
+
Existing ad hoc and Android commands keep their methods and `.build/adhoc/` outputs. A failed build
|
|
124
|
+
keeps the previous final IPA/APK. Command/configuration tests do not verify real signing, App Store
|
|
125
|
+
Connect acceptance, or TestFlight push delivery.
|
|
126
|
+
|
|
127
|
+
## Native stories
|
|
128
|
+
|
|
129
|
+
Each screen has a story beside it (`src/screens/*.stories.tsx`), and po-task has three `*.native.stories.tsx` stories for its native task UI. They are tagged `style-system`, `mockup`, and `native`. They render through `@agimon-ai/style-system`'s native mode: this package's `style-system.config.yaml` extends the root `native-app` preset (`bundler: vite-react-native`, `unistylesConfig: src/unistyles.ts`, dark scheme, 393 by 852 viewport). Whether style-system accepts po-task's stories, which live in another package, when rendered against this app path is unverified.
|
|
130
|
+
|
|
131
|
+
## Not yet
|
|
132
|
+
|
|
133
|
+
- **Activity groups are not rendered.** Only the plain host `activity` slot is; `activity.<group>` fills such as po-task's task section do not appear.
|
|
134
|
+
- **Threads, transient tabs, and composer context are inert placeholders.** The slot props the app passes to plugins stub `renderThread`, `openTransientTab`, `closeTransientTab`, `appendComposerDraft`, `attachComposerContext`, and `attachComposerCapture`; `sendSessionFrame`, `renderSlot`, and `slotData` are inert there too.
|
|
135
|
+
- **A session's composition mounts only if its summary is loaded when the session is opened.** A summary that arrives later does not mount it.
|
|
136
|
+
- **Not ported from po-web:** alerts, voice handoff, dormant transcripts, thread frames, and held channels.
|
|
137
|
+
- **`po doctor` does not show the native build.** `po sync --check` does.
|
|
138
|
+
- **Device runtime is unverified.** No release-mode device run has confirmed that Hermes runs `new Function`, that `react-native-quick-crypto` WebCrypto ECDSA produces the P1363 signatures the host verifies, that `expo-secure-store` biometric items prompt and invalidate as expected, or that the `expo-camera` scanner works.
|
|
139
|
+
- **Visual fidelity is BLOCKED.** The screens and stories are mockups (`mockup` tag). Rendered appearance needs human approval of the native stories before it can be claimed.
|
|
140
|
+
- **One paired host per phone.**
|
|
141
|
+
|
|
142
|
+
## Phase 3 native grants
|
|
143
|
+
|
|
144
|
+
`accountGrants` exposes `onSessionStart` and `onJobCreated` typed hooks. Same-host work returns
|
|
145
|
+
no grant and does not contact the account service. Cross-host work gets a 30-day, non-redelegable
|
|
146
|
+
C5 signed by the account device key with the issuer's current revocation epoch. SecureStore keeps
|
|
147
|
+
envelopes in issuer-specific namespaces. Opening or foregrounding the app renews unexpired grants
|
|
148
|
+
only inside the seven-day window. An epoch change never silently renews old consent. An explicit
|
|
149
|
+
renew action may obtain new consent after refreshing account trust.
|
|
150
|
+
|
|
151
|
+
`receiveGrantPause` accepts the typed work-paused event plus the authenticated reporting host id,
|
|
152
|
+
matches it to saved work, and supplies Home's gentle rose notice. `startLiveRuntime` accepts an
|
|
153
|
+
optional network-authenticated `federationHostId`. The local pairing record id is not that id.
|
|
154
|
+
|
|
155
|
+
Cross-host session/job creation and network host-id mapping are pending phases 5 and 7. Those
|
|
156
|
+
callers must pass the returned envelopes to the calling host, which stores them and refreshes
|
|
157
|
+
delegated tokens. App-open renewal currently updates the phone's saved envelopes only; host
|
|
158
|
+
handoff and automatic job resume are not mounted yet. No public host upload route is invented.
|
|
159
|
+
The notice deliberately says permission was renewed on this phone, not that a job resumed.
|
|
160
|
+
|
|
161
|
+
Notice targets: `Native/Design/Grants`, Playground, Dark, Renewing and Retry. These remain mockups.
|
|
162
|
+
Visual fidelity is BLOCKED pending explicit user approval, and native device behavior is unverified.
|
package/app.config.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ExpoConfig } from 'expo/config';
|
|
2
|
+
|
|
3
|
+
const apnsEnvironment = process.env.PO_NATIVE_APNS_ENVIRONMENT ?? 'development';
|
|
4
|
+
if (apnsEnvironment !== 'development' && apnsEnvironment !== 'production') {
|
|
5
|
+
throw new Error('PO_NATIVE_APNS_ENVIRONMENT must be development or production.');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const iosBuildNumber = process.env.PO_NATIVE_IOS_BUILD_NUMBER?.trim();
|
|
9
|
+
if (iosBuildNumber !== undefined && !/^[1-9]\d{0,3}(?:\.\d{1,2}){0,2}$/u.test(iosBuildNumber)) {
|
|
10
|
+
throw new Error('PO_NATIVE_IOS_BUILD_NUMBER must be a valid CFBundleVersion (for example 42).');
|
|
11
|
+
}
|
|
12
|
+
// A production build must name its identity home explicitly; the default issuer is for development only.
|
|
13
|
+
if (apnsEnvironment === 'production' && !process.env.PO_NATIVE_IDENTITY_ISSUER?.trim()) {
|
|
14
|
+
throw new Error('PO_NATIVE_IDENTITY_ISSUER must be set when PO_NATIVE_APNS_ENVIRONMENT is production.');
|
|
15
|
+
}
|
|
16
|
+
const identityIssuer = new URL(process.env.PO_NATIVE_IDENTITY_ISSUER ?? 'https://po.agimon.win');
|
|
17
|
+
if (
|
|
18
|
+
identityIssuer.protocol !== 'https:' ||
|
|
19
|
+
identityIssuer.username ||
|
|
20
|
+
identityIssuer.password ||
|
|
21
|
+
identityIssuer.pathname !== '/' ||
|
|
22
|
+
identityIssuer.search ||
|
|
23
|
+
identityIssuer.hash
|
|
24
|
+
) {
|
|
25
|
+
throw new Error('PO_NATIVE_IDENTITY_ISSUER must be an HTTPS origin.');
|
|
26
|
+
}
|
|
27
|
+
const config: ExpoConfig = {
|
|
28
|
+
name: 'Po',
|
|
29
|
+
slug: 'po',
|
|
30
|
+
scheme: 'po',
|
|
31
|
+
extra: { identityIssuer: identityIssuer.origin },
|
|
32
|
+
version: '0.0.1',
|
|
33
|
+
icon: './assets/icon.png',
|
|
34
|
+
orientation: 'portrait',
|
|
35
|
+
platforms: ['ios', 'android'],
|
|
36
|
+
ios: {
|
|
37
|
+
bundleIdentifier: 'ai.po.app',
|
|
38
|
+
usesAppleSignIn: true,
|
|
39
|
+
// Export compliance answer on App Store Connect: no non-exempt encryption.
|
|
40
|
+
config: { usesNonExemptEncryption: false },
|
|
41
|
+
...(iosBuildNumber ? { buildNumber: iosBuildNumber } : {}),
|
|
42
|
+
...(process.env.APPLE_TEAM_ID ? { appleTeamId: process.env.APPLE_TEAM_ID } : {}),
|
|
43
|
+
},
|
|
44
|
+
android: {
|
|
45
|
+
package: 'ai.agimon.po',
|
|
46
|
+
...(process.env.PO_NATIVE_ANDROID_GOOGLE_SERVICES_FILE
|
|
47
|
+
? { googleServicesFile: process.env.PO_NATIVE_ANDROID_GOOGLE_SERVICES_FILE }
|
|
48
|
+
: {}),
|
|
49
|
+
blockedPermissions: ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'],
|
|
50
|
+
},
|
|
51
|
+
plugins: [
|
|
52
|
+
'expo-apple-authentication',
|
|
53
|
+
['expo-notifications', { mode: apnsEnvironment, defaultChannel: 'po-notifications' }],
|
|
54
|
+
['expo-secure-store', { faceIDPermission: 'Confirm it is you before Po does something important.' }],
|
|
55
|
+
['expo-camera', { cameraPermission: 'Scan a Po pairing QR code.' }],
|
|
56
|
+
[
|
|
57
|
+
'expo-image-picker',
|
|
58
|
+
{
|
|
59
|
+
photosPermission: 'Attach photos to your message for Po.',
|
|
60
|
+
cameraPermission: false,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
'react-native-audio-api',
|
|
65
|
+
{
|
|
66
|
+
iosMicrophonePermission: 'Record your voice for Po while you choose to record.',
|
|
67
|
+
iosBackgroundMode: true,
|
|
68
|
+
androidPermissions: [
|
|
69
|
+
'android.permission.RECORD_AUDIO',
|
|
70
|
+
'android.permission.FOREGROUND_SERVICE',
|
|
71
|
+
'android.permission.FOREGROUND_SERVICE_MICROPHONE',
|
|
72
|
+
'android.permission.POST_NOTIFICATIONS',
|
|
73
|
+
],
|
|
74
|
+
androidForegroundService: true,
|
|
75
|
+
androidFSTypes: ['microphone'],
|
|
76
|
+
disableFFmpeg: true,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
'@config-plugins/react-native-webrtc',
|
|
80
|
+
'@agimon-ai/po-expo-signing',
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default config;
|
package/assets/icon.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/babel.config.cjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// babel-preset-expo already registers the react-native-worklets plugin; do not add it here.
|
|
2
|
+
module.exports = function babelConfig(api) {
|
|
3
|
+
api.cache(true);
|
|
4
|
+
return {
|
|
5
|
+
presets: ['babel-preset-expo'],
|
|
6
|
+
plugins: [['react-native-unistyles/plugin', { root: 'src' }]],
|
|
7
|
+
};
|
|
8
|
+
};
|