@steipete/oracle 1.1.0 → 1.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 (58) hide show
  1. package/README.md +29 -4
  2. package/assets-oracle-icon.png +0 -0
  3. package/dist/bin/oracle-cli.js +169 -18
  4. package/dist/bin/oracle-mcp.js +6 -0
  5. package/dist/src/browser/actions/modelSelection.js +117 -29
  6. package/dist/src/browser/cookies.js +1 -1
  7. package/dist/src/browser/index.js +2 -1
  8. package/dist/src/browser/prompt.js +6 -5
  9. package/dist/src/browser/sessionRunner.js +4 -2
  10. package/dist/src/cli/dryRun.js +41 -5
  11. package/dist/src/cli/engine.js +7 -0
  12. package/dist/src/cli/help.js +1 -1
  13. package/dist/src/cli/hiddenAliases.js +17 -0
  14. package/dist/src/cli/markdownRenderer.js +79 -0
  15. package/dist/src/cli/notifier.js +223 -0
  16. package/dist/src/cli/promptRequirement.js +3 -0
  17. package/dist/src/cli/runOptions.js +29 -0
  18. package/dist/src/cli/sessionCommand.js +1 -1
  19. package/dist/src/cli/sessionDisplay.js +94 -7
  20. package/dist/src/cli/sessionRunner.js +21 -2
  21. package/dist/src/cli/tui/index.js +436 -0
  22. package/dist/src/config.js +27 -0
  23. package/dist/src/mcp/server.js +36 -0
  24. package/dist/src/mcp/tools/consult.js +158 -0
  25. package/dist/src/mcp/tools/sessionResources.js +64 -0
  26. package/dist/src/mcp/tools/sessions.js +106 -0
  27. package/dist/src/mcp/types.js +17 -0
  28. package/dist/src/mcp/utils.js +24 -0
  29. package/dist/src/oracle/files.js +143 -6
  30. package/dist/src/oracle/run.js +41 -20
  31. package/dist/src/oracle/tokenEstimate.js +34 -0
  32. package/dist/src/sessionManager.js +48 -3
  33. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  34. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +20 -0
  35. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  36. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  37. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +128 -0
  38. package/dist/vendor/oracle-notifier/OracleNotifier.swift +45 -0
  39. package/dist/vendor/oracle-notifier/README.md +24 -0
  40. package/dist/vendor/oracle-notifier/build-notifier.sh +93 -0
  41. package/dist/vendor/oracle-notifier/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  42. package/dist/vendor/oracle-notifier/oracle-notifier/OracleNotifier.app/Contents/Info.plist +20 -0
  43. package/dist/vendor/oracle-notifier/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  44. package/dist/vendor/oracle-notifier/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  45. package/dist/vendor/oracle-notifier/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +128 -0
  46. package/dist/vendor/oracle-notifier/oracle-notifier/OracleNotifier.swift +45 -0
  47. package/dist/vendor/oracle-notifier/oracle-notifier/README.md +24 -0
  48. package/dist/vendor/oracle-notifier/oracle-notifier/build-notifier.sh +93 -0
  49. package/package.json +39 -13
  50. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  51. package/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +20 -0
  52. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  53. package/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  54. package/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +128 -0
  55. package/vendor/oracle-notifier/OracleNotifier.swift +45 -0
  56. package/vendor/oracle-notifier/README.md +24 -0
  57. package/vendor/oracle-notifier/build-notifier.sh +93 -0
  58. package/dist/.DS_Store +0 -0
@@ -0,0 +1,128 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>files</key>
6
+ <dict>
7
+ <key>Resources/OracleIcon.icns</key>
8
+ <data>
9
+ edUHAMetayIv3xtc3Vb92VXRLfM=
10
+ </data>
11
+ </dict>
12
+ <key>files2</key>
13
+ <dict>
14
+ <key>Resources/OracleIcon.icns</key>
15
+ <dict>
16
+ <key>hash2</key>
17
+ <data>
18
+ AVPJK/6w6IOsDLmZTW4hL+Za+/4wHMxZiIp0t6m3NRA=
19
+ </data>
20
+ </dict>
21
+ </dict>
22
+ <key>rules</key>
23
+ <dict>
24
+ <key>^Resources/</key>
25
+ <true/>
26
+ <key>^Resources/.*\.lproj/</key>
27
+ <dict>
28
+ <key>optional</key>
29
+ <true/>
30
+ <key>weight</key>
31
+ <real>1000</real>
32
+ </dict>
33
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
34
+ <dict>
35
+ <key>omit</key>
36
+ <true/>
37
+ <key>weight</key>
38
+ <real>1100</real>
39
+ </dict>
40
+ <key>^Resources/Base\.lproj/</key>
41
+ <dict>
42
+ <key>weight</key>
43
+ <real>1010</real>
44
+ </dict>
45
+ <key>^version.plist$</key>
46
+ <true/>
47
+ </dict>
48
+ <key>rules2</key>
49
+ <dict>
50
+ <key>.*\.dSYM($|/)</key>
51
+ <dict>
52
+ <key>weight</key>
53
+ <real>11</real>
54
+ </dict>
55
+ <key>^(.*/)?\.DS_Store$</key>
56
+ <dict>
57
+ <key>omit</key>
58
+ <true/>
59
+ <key>weight</key>
60
+ <real>2000</real>
61
+ </dict>
62
+ <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
63
+ <dict>
64
+ <key>nested</key>
65
+ <true/>
66
+ <key>weight</key>
67
+ <real>10</real>
68
+ </dict>
69
+ <key>^.*</key>
70
+ <true/>
71
+ <key>^Info\.plist$</key>
72
+ <dict>
73
+ <key>omit</key>
74
+ <true/>
75
+ <key>weight</key>
76
+ <real>20</real>
77
+ </dict>
78
+ <key>^PkgInfo$</key>
79
+ <dict>
80
+ <key>omit</key>
81
+ <true/>
82
+ <key>weight</key>
83
+ <real>20</real>
84
+ </dict>
85
+ <key>^Resources/</key>
86
+ <dict>
87
+ <key>weight</key>
88
+ <real>20</real>
89
+ </dict>
90
+ <key>^Resources/.*\.lproj/</key>
91
+ <dict>
92
+ <key>optional</key>
93
+ <true/>
94
+ <key>weight</key>
95
+ <real>1000</real>
96
+ </dict>
97
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
98
+ <dict>
99
+ <key>omit</key>
100
+ <true/>
101
+ <key>weight</key>
102
+ <real>1100</real>
103
+ </dict>
104
+ <key>^Resources/Base\.lproj/</key>
105
+ <dict>
106
+ <key>weight</key>
107
+ <real>1010</real>
108
+ </dict>
109
+ <key>^[^/]+$</key>
110
+ <dict>
111
+ <key>nested</key>
112
+ <true/>
113
+ <key>weight</key>
114
+ <real>10</real>
115
+ </dict>
116
+ <key>^embedded\.provisionprofile$</key>
117
+ <dict>
118
+ <key>weight</key>
119
+ <real>20</real>
120
+ </dict>
121
+ <key>^version\.plist$</key>
122
+ <dict>
123
+ <key>weight</key>
124
+ <real>20</real>
125
+ </dict>
126
+ </dict>
127
+ </dict>
128
+ </plist>
@@ -0,0 +1,45 @@
1
+ import Foundation
2
+ import UserNotifications
3
+
4
+ let args = CommandLine.arguments
5
+ // Usage: OracleNotifier <title> <message> [soundName]
6
+ if args.count < 3 {
7
+ fputs("usage: OracleNotifier <title> <message> [soundName]\n", stderr)
8
+ exit(1)
9
+ }
10
+ let title = args[1]
11
+ let message = args[2]
12
+ let soundName = args.count >= 4 ? args[3] : "Glass"
13
+
14
+ let center = UNUserNotificationCenter.current()
15
+ let group = DispatchGroup()
16
+ group.enter()
17
+ center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
18
+ if let error = error {
19
+ fputs("auth error: \(error)\n", stderr)
20
+ group.leave()
21
+ return
22
+ }
23
+ if !granted {
24
+ fputs("authorization not granted\n", stderr)
25
+ group.leave()
26
+ return
27
+ }
28
+ let content = UNMutableNotificationContent()
29
+ content.title = title
30
+ content.body = message
31
+ if !soundName.isEmpty {
32
+ content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: soundName))
33
+ } else {
34
+ content.sound = UNNotificationSound.default
35
+ }
36
+ let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
37
+ center.add(request) { addError in
38
+ if let addError = addError {
39
+ fputs("add error: \(addError)\n", stderr)
40
+ }
41
+ group.leave()
42
+ }
43
+ }
44
+ _ = group.wait(timeout: .now() + 2)
45
+ RunLoop.current.run(until: Date().addingTimeInterval(1))
@@ -0,0 +1,24 @@
1
+ # Oracle Notifier helper (macOS, arm64)
2
+
3
+ Builds a tiny signed helper app for macOS notifications with the Oracle icon.
4
+
5
+ ## Build
6
+
7
+ ```bash
8
+ cd vendor/oracle-notifier
9
+ # Optional: notarize by setting App Store Connect key credentials
10
+ export APP_STORE_CONNECT_API_KEY_P8="$(cat AuthKey_XXXXXX.p8)" # with literal newlines or \n escaped
11
+ export APP_STORE_CONNECT_KEY_ID=XXXXXX
12
+ export APP_STORE_CONNECT_ISSUER_ID=YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY
13
+ ./build-notifier.sh
14
+ ```
15
+
16
+ - Requires Xcode command line tools (swiftc) and a macOS Developer ID certificate. Without a valid cert, the build fails (no ad-hoc fallback).
17
+ - If `APP_STORE_CONNECT_*` vars are set, the script notarizes and staples the ticket.
18
+ - Output: `OracleNotifier.app` (arm64 only), bundled with `OracleIcon.icns`.
19
+
20
+ ## Usage
21
+ The CLI prefers this helper on macOS; if it fails or is missing, it falls back to toasted-notifier/terminal-notifier.
22
+
23
+ ## Permissions
24
+ After first run, allow notifications for “Oracle Notifier” in System Settings → Notifications.
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ cd "$(dirname "$0")"
4
+
5
+ ICON_SRC=../../assets-oracle-icon.png
6
+ APP=OracleNotifier.app
7
+ CONTENTS="$APP/Contents"
8
+ MACOS="$CONTENTS/MacOS"
9
+ RESOURCES="$CONTENTS/Resources"
10
+ ICONSET=OracleIcon.iconset
11
+ ICNS=OracleIcon.icns
12
+ IDENTITY="${CODESIGN_ID:-Developer ID Application: Peter Steinberger (Y5PE65HELJ)}"
13
+ ZIP="/tmp/OracleNotifierNotarize.zip"
14
+
15
+ NOTARY_KEY_P8="${APP_STORE_CONNECT_API_KEY_P8:-}"
16
+ NOTARY_KEY_ID="${APP_STORE_CONNECT_KEY_ID:-}"
17
+ NOTARY_ISSUER_ID="${APP_STORE_CONNECT_ISSUER_ID:-}"
18
+ DITTO_BIN=${DITTO_BIN:-/usr/bin/ditto}
19
+
20
+ cleanup() {
21
+ rm -f "$ZIP" /tmp/oracle-notifier-api-key.p8
22
+ }
23
+ trap cleanup EXIT
24
+
25
+ rm -rf "$APP" "$ICONSET" "$ICNS"
26
+ mkdir -p "$MACOS" "$RESOURCES"
27
+
28
+ # Build ICNS from PNG
29
+ mkdir "$ICONSET"
30
+ for sz in 16 32 64 128 256 512; do
31
+ sips -z $sz $sz "$ICON_SRC" --out "$ICONSET/icon_${sz}x${sz}.png" >/dev/null
32
+ sips -z $((sz*2)) $((sz*2)) "$ICON_SRC" --out "$ICONSET/icon_${sz}x${sz}@2x.png" >/dev/null
33
+ done
34
+ iconutil -c icns --output "$ICNS" "$ICONSET"
35
+ mv "$ICNS" "$RESOURCES/OracleIcon.icns"
36
+ rm -rf "$ICONSET"
37
+
38
+ # Write Info.plist
39
+ cat > "$CONTENTS/Info.plist" <<'PLIST'
40
+ <?xml version="1.0" encoding="UTF-8"?>
41
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
42
+ <plist version="1.0">
43
+ <dict>
44
+ <key>CFBundleIdentifier</key>
45
+ <string>com.steipete.oracle.notifier</string>
46
+ <key>CFBundleName</key>
47
+ <string>OracleNotifier</string>
48
+ <key>CFBundleDisplayName</key>
49
+ <string>Oracle Notifier</string>
50
+ <key>CFBundleExecutable</key>
51
+ <string>OracleNotifier</string>
52
+ <key>CFBundleIconFile</key>
53
+ <string>OracleIcon</string>
54
+ <key>CFBundlePackageType</key>
55
+ <string>APPL</string>
56
+ <key>LSMinimumSystemVersion</key>
57
+ <string>13.0</string>
58
+ </dict>
59
+ </plist>
60
+ PLIST
61
+
62
+ # Compile Swift helper (arm64)
63
+ swiftc -target arm64-apple-macos13 -o "$MACOS/OracleNotifier" OracleNotifier.swift -framework Foundation -framework UserNotifications
64
+
65
+ echo "Signing with $IDENTITY"
66
+ if ! codesign --force --deep --options runtime --timestamp --sign "$IDENTITY" "$APP"; then
67
+ echo "codesign failed. Set CODESIGN_ID to a valid Developer ID Application certificate." >&2
68
+ exit 1
69
+ fi
70
+
71
+ # Notarize if credentials are provided
72
+ if [[ -n "$NOTARY_KEY_P8" && -n "$NOTARY_KEY_ID" && -n "$NOTARY_ISSUER_ID" ]]; then
73
+ echo "$NOTARY_KEY_P8" | sed 's/\\n/\n/g' > /tmp/oracle-notifier-api-key.p8
74
+ echo "Packaging for notarization"
75
+ "$DITTO_BIN" -c -k --keepParent --sequesterRsrc "$APP" "$ZIP"
76
+
77
+ echo "Submitting for notarization"
78
+ xcrun notarytool submit "$ZIP" \
79
+ --key /tmp/oracle-notifier-api-key.p8 \
80
+ --key-id "$NOTARY_KEY_ID" \
81
+ --issuer "$NOTARY_ISSUER_ID" \
82
+ --wait
83
+
84
+ echo "Stapling ticket"
85
+ xcrun stapler staple "$APP"
86
+ xcrun stapler validate "$APP"
87
+ else
88
+ echo "Skipping notarization (APP_STORE_CONNECT_* env vars not set)."
89
+ fi
90
+
91
+ spctl -a -t exec -vv "$APP" || true
92
+
93
+ echo "Built $APP"
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleIdentifier</key>
6
+ <string>com.steipete.oracle.notifier</string>
7
+ <key>CFBundleName</key>
8
+ <string>OracleNotifier</string>
9
+ <key>CFBundleDisplayName</key>
10
+ <string>Oracle Notifier</string>
11
+ <key>CFBundleExecutable</key>
12
+ <string>OracleNotifier</string>
13
+ <key>CFBundleIconFile</key>
14
+ <string>OracleIcon</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>APPL</string>
17
+ <key>LSMinimumSystemVersion</key>
18
+ <string>13.0</string>
19
+ </dict>
20
+ </plist>
@@ -0,0 +1,128 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>files</key>
6
+ <dict>
7
+ <key>Resources/OracleIcon.icns</key>
8
+ <data>
9
+ edUHAMetayIv3xtc3Vb92VXRLfM=
10
+ </data>
11
+ </dict>
12
+ <key>files2</key>
13
+ <dict>
14
+ <key>Resources/OracleIcon.icns</key>
15
+ <dict>
16
+ <key>hash2</key>
17
+ <data>
18
+ AVPJK/6w6IOsDLmZTW4hL+Za+/4wHMxZiIp0t6m3NRA=
19
+ </data>
20
+ </dict>
21
+ </dict>
22
+ <key>rules</key>
23
+ <dict>
24
+ <key>^Resources/</key>
25
+ <true/>
26
+ <key>^Resources/.*\.lproj/</key>
27
+ <dict>
28
+ <key>optional</key>
29
+ <true/>
30
+ <key>weight</key>
31
+ <real>1000</real>
32
+ </dict>
33
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
34
+ <dict>
35
+ <key>omit</key>
36
+ <true/>
37
+ <key>weight</key>
38
+ <real>1100</real>
39
+ </dict>
40
+ <key>^Resources/Base\.lproj/</key>
41
+ <dict>
42
+ <key>weight</key>
43
+ <real>1010</real>
44
+ </dict>
45
+ <key>^version.plist$</key>
46
+ <true/>
47
+ </dict>
48
+ <key>rules2</key>
49
+ <dict>
50
+ <key>.*\.dSYM($|/)</key>
51
+ <dict>
52
+ <key>weight</key>
53
+ <real>11</real>
54
+ </dict>
55
+ <key>^(.*/)?\.DS_Store$</key>
56
+ <dict>
57
+ <key>omit</key>
58
+ <true/>
59
+ <key>weight</key>
60
+ <real>2000</real>
61
+ </dict>
62
+ <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
63
+ <dict>
64
+ <key>nested</key>
65
+ <true/>
66
+ <key>weight</key>
67
+ <real>10</real>
68
+ </dict>
69
+ <key>^.*</key>
70
+ <true/>
71
+ <key>^Info\.plist$</key>
72
+ <dict>
73
+ <key>omit</key>
74
+ <true/>
75
+ <key>weight</key>
76
+ <real>20</real>
77
+ </dict>
78
+ <key>^PkgInfo$</key>
79
+ <dict>
80
+ <key>omit</key>
81
+ <true/>
82
+ <key>weight</key>
83
+ <real>20</real>
84
+ </dict>
85
+ <key>^Resources/</key>
86
+ <dict>
87
+ <key>weight</key>
88
+ <real>20</real>
89
+ </dict>
90
+ <key>^Resources/.*\.lproj/</key>
91
+ <dict>
92
+ <key>optional</key>
93
+ <true/>
94
+ <key>weight</key>
95
+ <real>1000</real>
96
+ </dict>
97
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
98
+ <dict>
99
+ <key>omit</key>
100
+ <true/>
101
+ <key>weight</key>
102
+ <real>1100</real>
103
+ </dict>
104
+ <key>^Resources/Base\.lproj/</key>
105
+ <dict>
106
+ <key>weight</key>
107
+ <real>1010</real>
108
+ </dict>
109
+ <key>^[^/]+$</key>
110
+ <dict>
111
+ <key>nested</key>
112
+ <true/>
113
+ <key>weight</key>
114
+ <real>10</real>
115
+ </dict>
116
+ <key>^embedded\.provisionprofile$</key>
117
+ <dict>
118
+ <key>weight</key>
119
+ <real>20</real>
120
+ </dict>
121
+ <key>^version\.plist$</key>
122
+ <dict>
123
+ <key>weight</key>
124
+ <real>20</real>
125
+ </dict>
126
+ </dict>
127
+ </dict>
128
+ </plist>
@@ -0,0 +1,45 @@
1
+ import Foundation
2
+ import UserNotifications
3
+
4
+ let args = CommandLine.arguments
5
+ // Usage: OracleNotifier <title> <message> [soundName]
6
+ if args.count < 3 {
7
+ fputs("usage: OracleNotifier <title> <message> [soundName]\n", stderr)
8
+ exit(1)
9
+ }
10
+ let title = args[1]
11
+ let message = args[2]
12
+ let soundName = args.count >= 4 ? args[3] : "Glass"
13
+
14
+ let center = UNUserNotificationCenter.current()
15
+ let group = DispatchGroup()
16
+ group.enter()
17
+ center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
18
+ if let error = error {
19
+ fputs("auth error: \(error)\n", stderr)
20
+ group.leave()
21
+ return
22
+ }
23
+ if !granted {
24
+ fputs("authorization not granted\n", stderr)
25
+ group.leave()
26
+ return
27
+ }
28
+ let content = UNMutableNotificationContent()
29
+ content.title = title
30
+ content.body = message
31
+ if !soundName.isEmpty {
32
+ content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: soundName))
33
+ } else {
34
+ content.sound = UNNotificationSound.default
35
+ }
36
+ let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
37
+ center.add(request) { addError in
38
+ if let addError = addError {
39
+ fputs("add error: \(addError)\n", stderr)
40
+ }
41
+ group.leave()
42
+ }
43
+ }
44
+ _ = group.wait(timeout: .now() + 2)
45
+ RunLoop.current.run(until: Date().addingTimeInterval(1))
@@ -0,0 +1,24 @@
1
+ # Oracle Notifier helper (macOS, arm64)
2
+
3
+ Builds a tiny signed helper app for macOS notifications with the Oracle icon.
4
+
5
+ ## Build
6
+
7
+ ```bash
8
+ cd vendor/oracle-notifier
9
+ # Optional: notarize by setting App Store Connect key credentials
10
+ export APP_STORE_CONNECT_API_KEY_P8="$(cat AuthKey_XXXXXX.p8)" # with literal newlines or \n escaped
11
+ export APP_STORE_CONNECT_KEY_ID=XXXXXX
12
+ export APP_STORE_CONNECT_ISSUER_ID=YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY
13
+ ./build-notifier.sh
14
+ ```
15
+
16
+ - Requires Xcode command line tools (swiftc) and a macOS Developer ID certificate. Without a valid cert, the build fails (no ad-hoc fallback).
17
+ - If `APP_STORE_CONNECT_*` vars are set, the script notarizes and staples the ticket.
18
+ - Output: `OracleNotifier.app` (arm64 only), bundled with `OracleIcon.icns`.
19
+
20
+ ## Usage
21
+ The CLI prefers this helper on macOS; if it fails or is missing, it falls back to toasted-notifier/terminal-notifier.
22
+
23
+ ## Permissions
24
+ After first run, allow notifications for “Oracle Notifier” in System Settings → Notifications.
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ cd "$(dirname "$0")"
4
+
5
+ ICON_SRC=../../assets-oracle-icon.png
6
+ APP=OracleNotifier.app
7
+ CONTENTS="$APP/Contents"
8
+ MACOS="$CONTENTS/MacOS"
9
+ RESOURCES="$CONTENTS/Resources"
10
+ ICONSET=OracleIcon.iconset
11
+ ICNS=OracleIcon.icns
12
+ IDENTITY="${CODESIGN_ID:-Developer ID Application: Peter Steinberger (Y5PE65HELJ)}"
13
+ ZIP="/tmp/OracleNotifierNotarize.zip"
14
+
15
+ NOTARY_KEY_P8="${APP_STORE_CONNECT_API_KEY_P8:-}"
16
+ NOTARY_KEY_ID="${APP_STORE_CONNECT_KEY_ID:-}"
17
+ NOTARY_ISSUER_ID="${APP_STORE_CONNECT_ISSUER_ID:-}"
18
+ DITTO_BIN=${DITTO_BIN:-/usr/bin/ditto}
19
+
20
+ cleanup() {
21
+ rm -f "$ZIP" /tmp/oracle-notifier-api-key.p8
22
+ }
23
+ trap cleanup EXIT
24
+
25
+ rm -rf "$APP" "$ICONSET" "$ICNS"
26
+ mkdir -p "$MACOS" "$RESOURCES"
27
+
28
+ # Build ICNS from PNG
29
+ mkdir "$ICONSET"
30
+ for sz in 16 32 64 128 256 512; do
31
+ sips -z $sz $sz "$ICON_SRC" --out "$ICONSET/icon_${sz}x${sz}.png" >/dev/null
32
+ sips -z $((sz*2)) $((sz*2)) "$ICON_SRC" --out "$ICONSET/icon_${sz}x${sz}@2x.png" >/dev/null
33
+ done
34
+ iconutil -c icns --output "$ICNS" "$ICONSET"
35
+ mv "$ICNS" "$RESOURCES/OracleIcon.icns"
36
+ rm -rf "$ICONSET"
37
+
38
+ # Write Info.plist
39
+ cat > "$CONTENTS/Info.plist" <<'PLIST'
40
+ <?xml version="1.0" encoding="UTF-8"?>
41
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
42
+ <plist version="1.0">
43
+ <dict>
44
+ <key>CFBundleIdentifier</key>
45
+ <string>com.steipete.oracle.notifier</string>
46
+ <key>CFBundleName</key>
47
+ <string>OracleNotifier</string>
48
+ <key>CFBundleDisplayName</key>
49
+ <string>Oracle Notifier</string>
50
+ <key>CFBundleExecutable</key>
51
+ <string>OracleNotifier</string>
52
+ <key>CFBundleIconFile</key>
53
+ <string>OracleIcon</string>
54
+ <key>CFBundlePackageType</key>
55
+ <string>APPL</string>
56
+ <key>LSMinimumSystemVersion</key>
57
+ <string>13.0</string>
58
+ </dict>
59
+ </plist>
60
+ PLIST
61
+
62
+ # Compile Swift helper (arm64)
63
+ swiftc -target arm64-apple-macos13 -o "$MACOS/OracleNotifier" OracleNotifier.swift -framework Foundation -framework UserNotifications
64
+
65
+ echo "Signing with $IDENTITY"
66
+ if ! codesign --force --deep --options runtime --timestamp --sign "$IDENTITY" "$APP"; then
67
+ echo "codesign failed. Set CODESIGN_ID to a valid Developer ID Application certificate." >&2
68
+ exit 1
69
+ fi
70
+
71
+ # Notarize if credentials are provided
72
+ if [[ -n "$NOTARY_KEY_P8" && -n "$NOTARY_KEY_ID" && -n "$NOTARY_ISSUER_ID" ]]; then
73
+ echo "$NOTARY_KEY_P8" | sed 's/\\n/\n/g' > /tmp/oracle-notifier-api-key.p8
74
+ echo "Packaging for notarization"
75
+ "$DITTO_BIN" -c -k --keepParent --sequesterRsrc "$APP" "$ZIP"
76
+
77
+ echo "Submitting for notarization"
78
+ xcrun notarytool submit "$ZIP" \
79
+ --key /tmp/oracle-notifier-api-key.p8 \
80
+ --key-id "$NOTARY_KEY_ID" \
81
+ --issuer "$NOTARY_ISSUER_ID" \
82
+ --wait
83
+
84
+ echo "Stapling ticket"
85
+ xcrun stapler staple "$APP"
86
+ xcrun stapler validate "$APP"
87
+ else
88
+ echo "Skipping notarization (APP_STORE_CONNECT_* env vars not set)."
89
+ fi
90
+
91
+ spctl -a -t exec -vv "$APP" || true
92
+
93
+ echo "Built $APP"