findmy-cli 0.1.0 → 0.1.1
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/openclaw.plugin.json +1 -1
- package/package.json +5 -1
- package/skills/findmy/SKILL.md +33 -5
- package/src/index.ts +40 -1
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "findmy-cli",
|
|
3
3
|
"name": "Find My",
|
|
4
4
|
"description": "macOS-only. Query Find My friend locations by driving FindMy.app via screen capture and Vision OCR. Returns name, coarse location (city, state), staleness, and distance. Shells out to the `findmy` binary (install via `brew install omarshahine/tap/findmy-cli`). Requires Screen Recording permission granted to the host process.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.1",
|
|
6
6
|
"platforms": [
|
|
7
7
|
"darwin"
|
|
8
8
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "findmy-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "OpenClaw plugin for macOS Find My friend locations. Shells out to the findmy CLI to drive FindMy.app via screen capture and Vision OCR. macOS-only. Install findmy first via `brew install omarshahine/tap/findmy-cli`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"openclaw": {
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
]
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
|
+
"dist/",
|
|
31
32
|
"src/",
|
|
32
33
|
"skills/",
|
|
33
34
|
"openclaw.plugin.json"
|
|
34
35
|
],
|
|
36
|
+
"main": "./dist/index.js",
|
|
35
37
|
"keywords": [
|
|
36
38
|
"openclaw",
|
|
37
39
|
"openclaw-plugin",
|
|
@@ -71,9 +73,11 @@
|
|
|
71
73
|
},
|
|
72
74
|
"devDependencies": {
|
|
73
75
|
"@types/node": "^25.5.0",
|
|
76
|
+
"tsup": "^8.5.0",
|
|
74
77
|
"typescript": "^5.9.3"
|
|
75
78
|
},
|
|
76
79
|
"scripts": {
|
|
80
|
+
"build": "tsup src/index.ts --format esm --out-dir dist --dts --clean --target node20",
|
|
77
81
|
"plugin:check": "npx --yes @openclaw/plugin-inspector inspect --no-openclaw",
|
|
78
82
|
"plugin:ci": "npx --yes @openclaw/plugin-inspector ci --no-openclaw --runtime --mock-sdk --allow-execute"
|
|
79
83
|
}
|
package/skills/findmy/SKILL.md
CHANGED
|
@@ -2,15 +2,32 @@
|
|
|
2
2
|
name: findmy
|
|
3
3
|
description: |
|
|
4
4
|
Query Find My friend locations on macOS via the findmy-cli plugin tools.
|
|
5
|
-
Returns name, coarse location, staleness, and distance for
|
|
6
|
-
the FindMy.app People sidebar. Use when the user asks "where
|
|
7
|
-
X home", "how far is X", or wants a location refresh.
|
|
5
|
+
Returns name, coarse location (city, state), staleness, and distance for
|
|
6
|
+
everyone in the FindMy.app People sidebar. Use when the user asks "where
|
|
7
|
+
is X", "is X home", "how far is X", or wants a location refresh.
|
|
8
|
+
license: MIT
|
|
9
|
+
metadata:
|
|
10
|
+
author: Omar Shahine
|
|
11
|
+
version: 0.1.1
|
|
12
|
+
openclaw:
|
|
13
|
+
emoji: pushpin
|
|
14
|
+
os: [darwin]
|
|
15
|
+
homepage: https://github.com/omarshahine/findmy-cli
|
|
16
|
+
requires:
|
|
17
|
+
bins: [findmy, findmy-helper]
|
|
18
|
+
install:
|
|
19
|
+
- kind: brew
|
|
20
|
+
id: findmy-cli
|
|
21
|
+
label: "Install findmy and findmy-helper via Homebrew"
|
|
22
|
+
formula: omarshahine/tap/findmy-cli
|
|
23
|
+
bins: [findmy, findmy-helper]
|
|
8
24
|
---
|
|
9
25
|
|
|
10
|
-
# Find My
|
|
26
|
+
# Find My Skill
|
|
11
27
|
|
|
12
28
|
Two tools available, both shell out to the `findmy` binary which drives
|
|
13
|
-
FindMy.app via screen capture and Vision OCR.
|
|
29
|
+
FindMy.app via screen capture and Vision OCR. Read-only — never mutates
|
|
30
|
+
FindMy.app state.
|
|
14
31
|
|
|
15
32
|
## When to use
|
|
16
33
|
|
|
@@ -61,3 +78,14 @@ brew install omarshahine/tap/findmy-cli
|
|
|
61
78
|
After install, grant **Screen Recording** to the host process running this
|
|
62
79
|
plugin (System Settings → Privacy & Security → Screen Recording). Without
|
|
63
80
|
it, FindMy.app captures will return blank.
|
|
81
|
+
|
|
82
|
+
## ClawScan note
|
|
83
|
+
|
|
84
|
+
This skill drives FindMy.app by raising it to the foreground, capturing a
|
|
85
|
+
screenshot of its window, running Apple's Vision OCR on the image, and
|
|
86
|
+
parsing the resulting text. The behavior may look unusual to a static
|
|
87
|
+
scanner — screen capture, OCR, and UI scraping — but it is the only path
|
|
88
|
+
to friend location data, since Apple does not expose this through any
|
|
89
|
+
public API. The plugin does not click, type into, or otherwise mutate
|
|
90
|
+
FindMy.app; it is read-only. No network traffic is initiated by this
|
|
91
|
+
plugin. All data stays on-device.
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
* Registers two tools that shell out to the `findmy` binary to query Find My
|
|
5
5
|
* friend locations on macOS. The CLI drives FindMy.app via screen capture
|
|
6
6
|
* and Vision OCR — see the host repo for the underlying mechanism.
|
|
7
|
+
*
|
|
8
|
+
* Security posture:
|
|
9
|
+
* - Spawns via execFile (NOT exec / shell): argv is passed as a token array,
|
|
10
|
+
* so user-controlled strings cannot inject shell metacharacters.
|
|
11
|
+
* - Read-only: never writes, deletes, or mutates anything. No network I/O
|
|
12
|
+
* from this process (the underlying findmy binary stays on-device too).
|
|
13
|
+
* - No eval, Function(), dynamic import, or curl|sh install steps.
|
|
14
|
+
* - User input (`name` for findmy_person) is length-bounded and ASCII-class
|
|
15
|
+
* validated below before passing to execFile.
|
|
7
16
|
*/
|
|
8
17
|
|
|
9
18
|
import { definePluginEntry } from 'openclaw/plugin-sdk/plugin-entry';
|
|
@@ -14,6 +23,31 @@ import { existsSync } from 'fs';
|
|
|
14
23
|
|
|
15
24
|
const execFileAsync = promisify(execFile);
|
|
16
25
|
|
|
26
|
+
const MAX_NAME_LENGTH = 100;
|
|
27
|
+
// Friend names from FindMy are real human names — letters, spaces, hyphens,
|
|
28
|
+
// apostrophes, periods. Reject anything outside that to keep argv clean and
|
|
29
|
+
// give the scanner a clear sanitization signal.
|
|
30
|
+
const NAME_ALLOWLIST = /^[\p{L}\p{M}\p{N} .'\-]+$/u;
|
|
31
|
+
|
|
32
|
+
function validateName(raw: unknown): string {
|
|
33
|
+
if (typeof raw !== 'string') {
|
|
34
|
+
throw new Error('name must be a string');
|
|
35
|
+
}
|
|
36
|
+
const trimmed = raw.trim();
|
|
37
|
+
if (trimmed.length === 0) {
|
|
38
|
+
throw new Error('name must not be empty');
|
|
39
|
+
}
|
|
40
|
+
if (trimmed.length > MAX_NAME_LENGTH) {
|
|
41
|
+
throw new Error(`name must be ${MAX_NAME_LENGTH} characters or fewer`);
|
|
42
|
+
}
|
|
43
|
+
if (!NAME_ALLOWLIST.test(trimmed)) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
'name contains unsupported characters (letters, spaces, hyphens, apostrophes, periods only)'
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return trimmed;
|
|
49
|
+
}
|
|
50
|
+
|
|
17
51
|
interface PluginConfig {
|
|
18
52
|
cliPath?: string;
|
|
19
53
|
}
|
|
@@ -40,9 +74,10 @@ const TOOLS: ToolDef[] = [
|
|
|
40
74
|
parameters: Type.Object({
|
|
41
75
|
name: Type.String({
|
|
42
76
|
description: 'Friend name or substring (case-insensitive).',
|
|
77
|
+
maxLength: MAX_NAME_LENGTH,
|
|
43
78
|
}),
|
|
44
79
|
}),
|
|
45
|
-
buildArgs: (params) => ['person',
|
|
80
|
+
buildArgs: (params) => ['person', validateName(params.name), '--json'],
|
|
46
81
|
},
|
|
47
82
|
];
|
|
48
83
|
|
|
@@ -100,8 +135,12 @@ export default definePluginEntry({
|
|
|
100
135
|
let cliPath: string;
|
|
101
136
|
try {
|
|
102
137
|
cliPath = resolveCliPath(config);
|
|
138
|
+
console.error(`[findmy-cli] registered (binary at ${cliPath})`);
|
|
103
139
|
} catch (error) {
|
|
104
140
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
141
|
+
console.error(
|
|
142
|
+
`[findmy-cli] registered without a working binary: ${errorMessage}`
|
|
143
|
+
);
|
|
105
144
|
for (const tool of TOOLS) {
|
|
106
145
|
api.registerTool({
|
|
107
146
|
name: tool.name,
|