@heysalad/sally 0.1.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/LICENSE +22 -0
- package/README.md +20 -0
- package/dist/commands/ai.d.ts +3 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +8 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +63 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/device.d.ts +15 -0
- package/dist/commands/device.d.ts.map +1 -0
- package/dist/commands/device.js +173 -0
- package/dist/commands/device.js.map +1 -0
- package/dist/commands/logs.d.ts +3 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +8 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/tunnel.d.ts +3 -0
- package/dist/commands/tunnel.d.ts.map +1 -0
- package/dist/commands/tunnel.js +39 -0
- package/dist/commands/tunnel.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/api.d.ts +23 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/api.js +32 -0
- package/dist/utils/api.js.map +1 -0
- package/dist/utils/config.d.ts +20 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +52 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +25 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/runtime.d.ts +9 -0
- package/dist/utils/runtime.d.ts.map +1 -0
- package/dist/utils/runtime.js +103 -0
- package/dist/utils/runtime.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HeySalad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @heysalad/sally
|
|
2
|
+
|
|
3
|
+
The Sally CLI gives HeySalad developers and open source users one entry point for device bridges, AI helpers, auth, tunnels, and logs.
|
|
4
|
+
|
|
5
|
+
## Available commands
|
|
6
|
+
|
|
7
|
+
- `sally device start|list|connect|stop`
|
|
8
|
+
- `sally auth login|logout|whoami`
|
|
9
|
+
- `sally tunnel list`
|
|
10
|
+
- `sally ai`
|
|
11
|
+
- `sally logs`
|
|
12
|
+
|
|
13
|
+
## Local usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm --dir packages/cli build
|
|
17
|
+
node packages/cli/dist/index.js device list
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Before publish, use the built entrypoint directly. After publish, the same commands will be available through the `sally` bin exposed by `@heysalad/sally`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/commands/ai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAMxD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function registerAiCommand(program) {
|
|
2
|
+
const command = program.command("ai").description("Run Sally AI utilities");
|
|
3
|
+
command.command("ask").description("Ask an AI provider").action(noop);
|
|
4
|
+
command.command("run").description("Run a named AI workflow").action(noop);
|
|
5
|
+
command.command("agent").description("Launch an AI sub-agent").action(noop);
|
|
6
|
+
}
|
|
7
|
+
function noop() { }
|
|
8
|
+
//# sourceMappingURL=ai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/commands/ai.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAChD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,wBAAwB,CAAC,CAAC;IAE5E,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,wBAAwB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,IAAI,KAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkD1D"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readConfig, updateConfig, writeConfig } from "../utils/config.js";
|
|
2
|
+
import { createLogger } from "../utils/logger.js";
|
|
3
|
+
export function registerAuthCommand(program) {
|
|
4
|
+
const command = program.command("auth").description("Manage Sally auth");
|
|
5
|
+
const logger = createLogger();
|
|
6
|
+
command
|
|
7
|
+
.command("login")
|
|
8
|
+
.description("Persist API endpoint and optional Access token locally")
|
|
9
|
+
.option("--api-base-url <url>", "Sally worker URL")
|
|
10
|
+
.option("--team-slug <slug>", "HeySalad team slug")
|
|
11
|
+
.option("--token <token>", "Cloudflare Access JWT or API bearer token")
|
|
12
|
+
.action(async (options) => {
|
|
13
|
+
const existing = await readConfig();
|
|
14
|
+
const config = await writeConfig({
|
|
15
|
+
apiBaseUrl: options.apiBaseUrl ?? existing.apiBaseUrl,
|
|
16
|
+
...(options.teamSlug ?? existing.teamSlug ? { teamSlug: options.teamSlug ?? existing.teamSlug } : {}),
|
|
17
|
+
...(options.token ?? existing.authToken ? { authToken: options.token ?? existing.authToken } : {}),
|
|
18
|
+
...(existing.activeDaemon ? { activeDaemon: existing.activeDaemon } : {})
|
|
19
|
+
});
|
|
20
|
+
logger.success(`Saved Sally config for ${config.apiBaseUrl}`);
|
|
21
|
+
});
|
|
22
|
+
command
|
|
23
|
+
.command("logout")
|
|
24
|
+
.description("Clear the stored auth token")
|
|
25
|
+
.action(async () => {
|
|
26
|
+
await updateConfig((config) => ({
|
|
27
|
+
...(config.activeDaemon ? { activeDaemon: config.activeDaemon } : {}),
|
|
28
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
29
|
+
...(config.teamSlug ? { teamSlug: config.teamSlug } : {})
|
|
30
|
+
}));
|
|
31
|
+
logger.success("Cleared local Sally auth token");
|
|
32
|
+
});
|
|
33
|
+
command
|
|
34
|
+
.command("whoami")
|
|
35
|
+
.description("Show the current Sally identity")
|
|
36
|
+
.action(async () => {
|
|
37
|
+
const config = await readConfig();
|
|
38
|
+
const claims = config.authToken ? decodeJwtPayload(config.authToken) : null;
|
|
39
|
+
logger.info(`API base URL: ${config.apiBaseUrl}`);
|
|
40
|
+
if (config.teamSlug) {
|
|
41
|
+
logger.info(`Team slug: ${config.teamSlug}`);
|
|
42
|
+
}
|
|
43
|
+
if (!claims) {
|
|
44
|
+
logger.warn("No auth token saved. Use `sally auth login --token <jwt>` for Access-backed commands.");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
logger.success(`Authenticated as ${claims.email ?? claims.sub ?? "unknown-user"}`);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function decodeJwtPayload(token) {
|
|
51
|
+
const segments = token.split(".");
|
|
52
|
+
const payload = segments[1];
|
|
53
|
+
if (!payload) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ConnectedDevice } from "@heysalad/sally-sdk";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import type { RemoteDeviceRecord } from "../utils/api.js";
|
|
4
|
+
interface DeviceView {
|
|
5
|
+
connected: boolean;
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
platform: "ios" | "android";
|
|
9
|
+
status: string;
|
|
10
|
+
tunnelUrl: string | null;
|
|
11
|
+
}
|
|
12
|
+
export declare function registerDeviceCommand(program: Command): void;
|
|
13
|
+
export declare function mergeDeviceViews(localDevices: ConnectedDevice[], remoteDevices: RemoteDeviceRecord[]): DeviceView[];
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/commands/device.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAc1D,UAAU,UAAU;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmC5D;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,eAAe,EAAE,EAC/B,aAAa,EAAE,kBAAkB,EAAE,GAClC,UAAU,EAAE,CA2Bd"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { DeviceManager } from "@heysalad/sally-sdk";
|
|
3
|
+
import { createApiClient } from "../utils/api.js";
|
|
4
|
+
import { readConfig, updateConfig } from "../utils/config.js";
|
|
5
|
+
import { createLogger } from "../utils/logger.js";
|
|
6
|
+
import { ensurePathExists, isProcessAlive, openExternal, resolveAgentEntry, resolveStreamWorkingDirectory, sleep, spawnDetached } from "../utils/runtime.js";
|
|
7
|
+
export function registerDeviceCommand(program) {
|
|
8
|
+
const command = program.command("device").description("Manage device sessions");
|
|
9
|
+
command
|
|
10
|
+
.command("start")
|
|
11
|
+
.description("Start the local Sally agent daemon")
|
|
12
|
+
.option("--foreground", "Run the device agent in the foreground", false)
|
|
13
|
+
.option("--mode <mode>", "Tunnel mode to use", "quick")
|
|
14
|
+
.option("--worker-url <url>", "Sally worker URL")
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
await startDeviceDaemon(options);
|
|
17
|
+
});
|
|
18
|
+
command
|
|
19
|
+
.command("list")
|
|
20
|
+
.description("List locally connected devices, enriched with worker status when configured")
|
|
21
|
+
.option("--json", "Print JSON output", false)
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
await listDevices(options);
|
|
24
|
+
});
|
|
25
|
+
command
|
|
26
|
+
.command("connect <id>")
|
|
27
|
+
.description("Open the active stream URL for a device in the default browser")
|
|
28
|
+
.option("--print-only", "Print the URL instead of opening it", false)
|
|
29
|
+
.action(async (id, options) => {
|
|
30
|
+
await connectToDevice(id, options);
|
|
31
|
+
});
|
|
32
|
+
command
|
|
33
|
+
.command("stop")
|
|
34
|
+
.description("Stop the locally managed Sally device agent")
|
|
35
|
+
.action(async () => {
|
|
36
|
+
await stopDeviceDaemon();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function mergeDeviceViews(localDevices, remoteDevices) {
|
|
40
|
+
const merged = new Map();
|
|
41
|
+
for (const device of remoteDevices) {
|
|
42
|
+
merged.set(device.id, {
|
|
43
|
+
connected: false,
|
|
44
|
+
id: device.id,
|
|
45
|
+
name: device.name,
|
|
46
|
+
platform: device.platform,
|
|
47
|
+
status: device.status,
|
|
48
|
+
tunnelUrl: device.tunnelUrl
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
for (const device of localDevices) {
|
|
52
|
+
const existing = merged.get(device.id);
|
|
53
|
+
merged.set(device.id, {
|
|
54
|
+
connected: true,
|
|
55
|
+
id: device.id,
|
|
56
|
+
name: device.name,
|
|
57
|
+
platform: device.platform,
|
|
58
|
+
status: existing?.status ?? "local",
|
|
59
|
+
tunnelUrl: existing?.tunnelUrl ?? null
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return [...merged.values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
63
|
+
}
|
|
64
|
+
async function startDeviceDaemon(options) {
|
|
65
|
+
const logger = createLogger();
|
|
66
|
+
const config = await readConfig();
|
|
67
|
+
if (config.activeDaemon && await isProcessAlive(config.activeDaemon.pid)) {
|
|
68
|
+
logger.warn(`Sally agent is already running with pid ${config.activeDaemon.pid}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const agentEntry = resolveAgentEntry();
|
|
72
|
+
if (!agentEntry || !await ensurePathExists(agentEntry)) {
|
|
73
|
+
throw new Error("Unable to resolve the built sally-agent entrypoint. Build packages/agent first.");
|
|
74
|
+
}
|
|
75
|
+
const workerUrl = options.workerUrl ?? process.env.SALLY_API_BASE_URL ?? config.apiBaseUrl;
|
|
76
|
+
const mode = options.mode ?? "quick";
|
|
77
|
+
const spinner = logger.start("Starting Sally device agent");
|
|
78
|
+
const environment = {
|
|
79
|
+
...process.env,
|
|
80
|
+
SALLY_STREAM_WORKDIR: resolveStreamWorkingDirectory(),
|
|
81
|
+
SALLY_TUNNEL_MODE: mode,
|
|
82
|
+
SALLY_WORKER_URL: workerUrl
|
|
83
|
+
};
|
|
84
|
+
if (options.foreground) {
|
|
85
|
+
spawn(process.execPath, [agentEntry], { env: environment, stdio: "inherit" });
|
|
86
|
+
spinner.succeed("Started Sally device agent in the foreground");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const child = spawnDetached(process.execPath, [agentEntry], { env: environment });
|
|
90
|
+
await updateConfig((current) => ({
|
|
91
|
+
...(current.authToken ? { authToken: current.authToken } : {}),
|
|
92
|
+
activeDaemon: {
|
|
93
|
+
mode,
|
|
94
|
+
pid: child.pid ?? 0,
|
|
95
|
+
startedAt: Date.now(),
|
|
96
|
+
workerUrl
|
|
97
|
+
},
|
|
98
|
+
apiBaseUrl: workerUrl,
|
|
99
|
+
...(current.teamSlug ? { teamSlug: current.teamSlug } : {})
|
|
100
|
+
}));
|
|
101
|
+
await sleep(2_000);
|
|
102
|
+
spinner.succeed(`Started Sally device agent in the background (pid ${child.pid ?? "unknown"})`);
|
|
103
|
+
}
|
|
104
|
+
async function listDevices(options) {
|
|
105
|
+
const logger = createLogger();
|
|
106
|
+
const config = await readConfig();
|
|
107
|
+
const deviceManager = new DeviceManager();
|
|
108
|
+
const localDevices = await deviceManager.list();
|
|
109
|
+
const remoteDevices = await tryListRemoteDevices(config.apiBaseUrl, config.authToken);
|
|
110
|
+
const devices = mergeDeviceViews(localDevices, remoteDevices);
|
|
111
|
+
if (options.json) {
|
|
112
|
+
console.log(JSON.stringify(devices, null, 2));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (devices.length === 0) {
|
|
116
|
+
logger.warn("No devices found locally or via the configured Sally worker.");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const device of devices) {
|
|
120
|
+
const tunnel = device.tunnelUrl ? ` ${device.tunnelUrl}` : "";
|
|
121
|
+
const connected = device.connected ? "connected" : "remote";
|
|
122
|
+
logger.info(`${device.id} ${device.platform} ${device.status} ${connected} ${device.name}${tunnel}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function connectToDevice(id, options) {
|
|
126
|
+
const logger = createLogger();
|
|
127
|
+
const config = await readConfig();
|
|
128
|
+
const remoteDevices = await tryListRemoteDevices(config.apiBaseUrl, config.authToken);
|
|
129
|
+
const device = remoteDevices.find((candidate) => candidate.id === id);
|
|
130
|
+
if (!device?.tunnelUrl) {
|
|
131
|
+
throw new Error(`No active stream URL found for device ${id}. Run \`sally device start\` first.`);
|
|
132
|
+
}
|
|
133
|
+
if (options.printOnly) {
|
|
134
|
+
console.log(device.tunnelUrl);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
await openExternal(device.tunnelUrl);
|
|
138
|
+
logger.success(`Opened ${device.tunnelUrl}`);
|
|
139
|
+
}
|
|
140
|
+
async function stopDeviceDaemon() {
|
|
141
|
+
const logger = createLogger();
|
|
142
|
+
const config = await readConfig();
|
|
143
|
+
const daemon = config.activeDaemon;
|
|
144
|
+
if (!daemon) {
|
|
145
|
+
logger.warn("No local Sally agent daemon is recorded in config.");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
process.kill(daemon.pid, "SIGTERM");
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
logger.warn(`Could not signal pid ${daemon.pid}; clearing local daemon metadata.`);
|
|
153
|
+
}
|
|
154
|
+
await updateConfig((current) => ({
|
|
155
|
+
apiBaseUrl: current.apiBaseUrl,
|
|
156
|
+
...(current.authToken ? { authToken: current.authToken } : {}),
|
|
157
|
+
...(current.teamSlug ? { teamSlug: current.teamSlug } : {})
|
|
158
|
+
}));
|
|
159
|
+
logger.success(`Stopped Sally device agent pid ${daemon.pid}`);
|
|
160
|
+
}
|
|
161
|
+
async function tryListRemoteDevices(apiBaseUrl, authToken) {
|
|
162
|
+
try {
|
|
163
|
+
const api = createApiClient({
|
|
164
|
+
baseUrl: apiBaseUrl,
|
|
165
|
+
...(authToken ? { accessToken: authToken } : {})
|
|
166
|
+
});
|
|
167
|
+
return await api.listDevices();
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/commands/device.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAM1D"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function registerLogsCommand(program) {
|
|
2
|
+
const command = program.command("logs").description("Inspect Sally logs");
|
|
3
|
+
command.command("device").description("Show device logs").action(noop);
|
|
4
|
+
command.command("session").description("Show session logs").action(noop);
|
|
5
|
+
command.command("audit").description("Show audit logs").action(noop);
|
|
6
|
+
}
|
|
7
|
+
function noop() { }
|
|
8
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC;IAE1E,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvE,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,IAAI,KAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["../../src/commands/tunnel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsC5D"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createApiClient } from "../utils/api.js";
|
|
2
|
+
import { readConfig } from "../utils/config.js";
|
|
3
|
+
import { createLogger } from "../utils/logger.js";
|
|
4
|
+
export function registerTunnelCommand(program) {
|
|
5
|
+
const command = program.command("tunnel").description("Manage Cloudflare tunnels");
|
|
6
|
+
const logger = createLogger();
|
|
7
|
+
command
|
|
8
|
+
.command("open")
|
|
9
|
+
.description("Alias for `sally device start`")
|
|
10
|
+
.action(() => {
|
|
11
|
+
logger.warn("Use `sally device start` to launch the local agent and open device tunnels.");
|
|
12
|
+
});
|
|
13
|
+
command
|
|
14
|
+
.command("close")
|
|
15
|
+
.description("Alias for `sally device stop`")
|
|
16
|
+
.action(() => {
|
|
17
|
+
logger.warn("Use `sally device stop` to shut down the local agent and close device tunnels.");
|
|
18
|
+
});
|
|
19
|
+
command
|
|
20
|
+
.command("list")
|
|
21
|
+
.description("List tunnels currently registered in the Sally worker")
|
|
22
|
+
.action(async () => {
|
|
23
|
+
const config = await readConfig();
|
|
24
|
+
const api = createApiClient({
|
|
25
|
+
baseUrl: config.apiBaseUrl,
|
|
26
|
+
...(config.authToken ? { accessToken: config.authToken } : {})
|
|
27
|
+
});
|
|
28
|
+
const devices = await api.listDevices();
|
|
29
|
+
const active = devices.filter((device) => device.tunnelUrl);
|
|
30
|
+
if (active.length === 0) {
|
|
31
|
+
logger.warn("No active tunnel URLs are registered.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
for (const device of active) {
|
|
35
|
+
logger.info(`${device.id} ${device.status} ${device.tunnelUrl ?? "n/a"}`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=tunnel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel.js","sourceRoot":"","sources":["../../src/commands/tunnel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAE9B,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,+BAA+B,CAAC;SAC5C,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,eAAe,CAAC;YAC1B,OAAO,EAAE,MAAM,CAAC,UAAU;YAC1B,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/D,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC5D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;YACrD,OAAO;QACT,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,IAAI,KAAK,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { registerAiCommand } from "./commands/ai.js";
|
|
5
|
+
import { registerAuthCommand } from "./commands/auth.js";
|
|
6
|
+
import { registerDeviceCommand } from "./commands/device.js";
|
|
7
|
+
import { registerLogsCommand } from "./commands/logs.js";
|
|
8
|
+
import { registerTunnelCommand } from "./commands/tunnel.js";
|
|
9
|
+
const program = new Command();
|
|
10
|
+
const packageVersion = readPackageVersion();
|
|
11
|
+
program
|
|
12
|
+
.name("sally")
|
|
13
|
+
.description("HeySalad Sally CLI")
|
|
14
|
+
.showHelpAfterError()
|
|
15
|
+
.version(packageVersion);
|
|
16
|
+
registerDeviceCommand(program);
|
|
17
|
+
registerAiCommand(program);
|
|
18
|
+
registerAuthCommand(program);
|
|
19
|
+
registerTunnelCommand(program);
|
|
20
|
+
registerLogsCommand(program);
|
|
21
|
+
void program.parseAsync(process.argv);
|
|
22
|
+
function readPackageVersion() {
|
|
23
|
+
const packageJsonUrl = new URL("../package.json", import.meta.url);
|
|
24
|
+
const packageJson = JSON.parse(readFileSync(packageJsonUrl, "utf8"));
|
|
25
|
+
return packageJson.version ?? "0.0.0";
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,MAAM,cAAc,GAAG,kBAAkB,EAAE,CAAC;AAE5C,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,oBAAoB,CAAC;KACjC,kBAAkB,EAAE;KACpB,OAAO,CAAC,cAAc,CAAC,CAAC;AAE3B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,iBAAiB,CAAC,OAAO,CAAC,CAAC;AAC3B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAE7B,KAAK,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtC,SAAS,kBAAkB;IACzB,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAElE,CAAC;IACF,OAAO,WAAW,CAAC,OAAO,IAAI,OAAO,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface RemoteDeviceRecord {
|
|
2
|
+
agentHost: string | null;
|
|
3
|
+
id: string;
|
|
4
|
+
lastSeen: number | null;
|
|
5
|
+
model: string | null;
|
|
6
|
+
name: string;
|
|
7
|
+
osVersion: string | null;
|
|
8
|
+
platform: "ios" | "android";
|
|
9
|
+
status: string;
|
|
10
|
+
teamId: string | null;
|
|
11
|
+
tunnelUrl: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface SallyApiClient {
|
|
14
|
+
listDevices(): Promise<RemoteDeviceRecord[]>;
|
|
15
|
+
ping(): Promise<string>;
|
|
16
|
+
}
|
|
17
|
+
export interface SallyApiClientOptions {
|
|
18
|
+
accessToken?: string;
|
|
19
|
+
baseUrl: string;
|
|
20
|
+
fetchImpl?: typeof fetch;
|
|
21
|
+
}
|
|
22
|
+
export declare function createApiClient(options: SallyApiClientOptions): SallyApiClient;
|
|
23
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/utils/api.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAC7C,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAoB9E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function createApiClient(options) {
|
|
2
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
3
|
+
const baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
4
|
+
return {
|
|
5
|
+
async listDevices() {
|
|
6
|
+
const response = await fetchImpl(`${baseUrl}/devices`, {
|
|
7
|
+
headers: buildHeaders(options.accessToken)
|
|
8
|
+
});
|
|
9
|
+
const payload = await parseJson(response);
|
|
10
|
+
return payload.items ?? [];
|
|
11
|
+
},
|
|
12
|
+
async ping() {
|
|
13
|
+
const response = await fetchImpl(`${baseUrl}/`, {
|
|
14
|
+
headers: buildHeaders(options.accessToken)
|
|
15
|
+
});
|
|
16
|
+
const payload = await parseJson(response);
|
|
17
|
+
return payload.service ?? "unknown";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function buildHeaders(accessToken) {
|
|
22
|
+
return accessToken
|
|
23
|
+
? { Authorization: `Bearer ${accessToken}` }
|
|
24
|
+
: {};
|
|
25
|
+
}
|
|
26
|
+
async function parseJson(response) {
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error(`Sally API request failed with status ${response.status}`);
|
|
29
|
+
}
|
|
30
|
+
return response.json();
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/utils/api.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,eAAe,CAAC,OAA8B;IAC5D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAEnD,OAAO;QACL,KAAK,CAAC,WAAW;YACf,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,OAAO,UAAU,EAAE;gBACrD,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC;aAC3C,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,SAAS,CAAmC,QAAQ,CAAC,CAAC;YAC5E,OAAO,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAC7B,CAAC;QACD,KAAK,CAAC,IAAI;YACR,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,OAAO,GAAG,EAAE;gBAC9C,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC;aAC3C,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,SAAS,CAAuB,QAAQ,CAAC,CAAC;YAChE,OAAO,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;QACtC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,WAAoB;IACxC,OAAO,WAAW;QAChB,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,WAAW,EAAE,EAAE;QAC5C,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED,KAAK,UAAU,SAAS,CAAI,QAAkB;IAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wCAAwC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAgB,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ActiveDaemonConfig {
|
|
2
|
+
mode: "auto" | "named" | "quick";
|
|
3
|
+
pid: number;
|
|
4
|
+
startedAt: number;
|
|
5
|
+
workerUrl: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SallyConfig {
|
|
8
|
+
activeDaemon?: ActiveDaemonConfig;
|
|
9
|
+
apiBaseUrl: string;
|
|
10
|
+
authToken?: string;
|
|
11
|
+
teamSlug?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function getDefaultConfig(): SallyConfig;
|
|
14
|
+
export declare function readConfig(): Promise<SallyConfig>;
|
|
15
|
+
export declare function writeConfig(config: SallyConfig): Promise<SallyConfig>;
|
|
16
|
+
export declare function updateConfig(update: (config: SallyConfig) => SallyConfig): Promise<SallyConfig>;
|
|
17
|
+
export declare function clearConfig(): Promise<void>;
|
|
18
|
+
export declare function getConfigDirectory(): string;
|
|
19
|
+
export declare function getConfigFilePath(): string;
|
|
20
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,IAAI,WAAW,CAI9C;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC,CAUvD;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAK3E;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,WAAW,GAC3C,OAAO,CAAC,WAAW,CAAC,CAGtB;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjD;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export function getDefaultConfig() {
|
|
5
|
+
return {
|
|
6
|
+
apiBaseUrl: process.env.SALLY_API_BASE_URL ?? "http://localhost:8787"
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export async function readConfig() {
|
|
10
|
+
try {
|
|
11
|
+
const raw = await readFile(getConfigFilePath(), "utf8");
|
|
12
|
+
return normalizeConfig(JSON.parse(raw));
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (isMissing(error)) {
|
|
16
|
+
return getDefaultConfig();
|
|
17
|
+
}
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function writeConfig(config) {
|
|
22
|
+
const normalized = normalizeConfig(config);
|
|
23
|
+
await mkdir(getConfigDirectory(), { recursive: true });
|
|
24
|
+
await writeFile(getConfigFilePath(), `${JSON.stringify(normalized, null, 2)}\n`, "utf8");
|
|
25
|
+
return normalized;
|
|
26
|
+
}
|
|
27
|
+
export async function updateConfig(update) {
|
|
28
|
+
const current = await readConfig();
|
|
29
|
+
return writeConfig(update(current));
|
|
30
|
+
}
|
|
31
|
+
export async function clearConfig() {
|
|
32
|
+
await rm(getConfigFilePath(), { force: true });
|
|
33
|
+
}
|
|
34
|
+
export function getConfigDirectory() {
|
|
35
|
+
return process.env.SALLY_CONFIG_HOME ?? path.join(os.homedir(), ".sally");
|
|
36
|
+
}
|
|
37
|
+
export function getConfigFilePath() {
|
|
38
|
+
return path.join(getConfigDirectory(), "config.json");
|
|
39
|
+
}
|
|
40
|
+
function normalizeConfig(config) {
|
|
41
|
+
const defaults = getDefaultConfig();
|
|
42
|
+
return {
|
|
43
|
+
...(config.activeDaemon ? { activeDaemon: config.activeDaemon } : {}),
|
|
44
|
+
apiBaseUrl: config.apiBaseUrl ?? defaults.apiBaseUrl,
|
|
45
|
+
...(config.authToken ? { authToken: config.authToken } : {}),
|
|
46
|
+
...(config.teamSlug ? { teamSlug: config.teamSlug } : {})
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function isMissing(error) {
|
|
50
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAgB7B,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,uBAAuB;KACtE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,iBAAiB,EAAE,EAAE,MAAM,CAAC,CAAC;QACxD,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,gBAAgB,EAAE,CAAC;QAC5B,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAmB;IACnD,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,KAAK,CAAC,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,SAAS,CAAC,iBAAiB,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACzF,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAA4C;IAE5C,MAAM,OAAO,GAAG,MAAM,UAAU,EAAE,CAAC;IACnC,OAAO,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,EAAE,CAAC,iBAAiB,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,aAAa,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,eAAe,CAAC,MAA4B;IACnD,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,OAAO;QACL,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU;QACpD,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1D,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Ora } from "ora";
|
|
2
|
+
export interface Logger {
|
|
3
|
+
error(message: string): void;
|
|
4
|
+
info(message: string): void;
|
|
5
|
+
start(message: string): Ora;
|
|
6
|
+
success(message: string): void;
|
|
7
|
+
warn(message: string): void;
|
|
8
|
+
}
|
|
9
|
+
export declare function createLogger(): Logger;
|
|
10
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AACA,OAAY,EAAE,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IAC5B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAgB,YAAY,IAAI,MAAM,CAqBrC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
export function createLogger() {
|
|
4
|
+
return {
|
|
5
|
+
error(message) {
|
|
6
|
+
console.error(chalk.red(message));
|
|
7
|
+
},
|
|
8
|
+
info(message) {
|
|
9
|
+
console.log(chalk.cyan(message));
|
|
10
|
+
},
|
|
11
|
+
start(message) {
|
|
12
|
+
return ora({
|
|
13
|
+
isEnabled: process.stdout.isTTY,
|
|
14
|
+
text: message
|
|
15
|
+
}).start();
|
|
16
|
+
},
|
|
17
|
+
success(message) {
|
|
18
|
+
console.log(chalk.green(message));
|
|
19
|
+
},
|
|
20
|
+
warn(message) {
|
|
21
|
+
console.warn(chalk.yellow(message));
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAiB,MAAM,KAAK,CAAC;AAUpC,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,KAAK,CAAC,OAAO;YACX,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,OAAO;YACV,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QACnC,CAAC;QACD,KAAK,CAAC,OAAO;YACX,OAAO,GAAG,CAAC;gBACT,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK;gBAC/B,IAAI,EAAE,OAAO;aACd,CAAC,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;QACD,OAAO,CAAC,OAAO;YACb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,OAAO;YACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ChildProcess, type SpawnOptions } from "node:child_process";
|
|
2
|
+
export declare function openExternal(url: string): Promise<void>;
|
|
3
|
+
export declare function resolveAgentEntry(): string | null;
|
|
4
|
+
export declare function resolveStreamWorkingDirectory(): string;
|
|
5
|
+
export declare function isProcessAlive(pid: number): Promise<boolean>;
|
|
6
|
+
export declare function ensurePathExists(targetPath: string): Promise<boolean>;
|
|
7
|
+
export declare function sleep(durationMs: number): Promise<void>;
|
|
8
|
+
export declare function spawnDetached(command: string, args: string[], options: SpawnOptions): ChildProcess;
|
|
9
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/utils/runtime.ts"],"names":[],"mappings":"AAGA,OAAO,EAAS,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGjF,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7D;AAED,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAYjD;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOlE;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO3E;AAED,wBAAgB,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvD;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,YAAY,GACpB,YAAY,CAQd"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { access } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
export async function openExternal(url) {
|
|
7
|
+
const { command, args } = getOpenCommand(url);
|
|
8
|
+
await spawnAndWait(command, args, { stdio: "ignore" });
|
|
9
|
+
}
|
|
10
|
+
export function resolveAgentEntry() {
|
|
11
|
+
const fromEnv = process.env.SALLY_AGENT_ENTRY;
|
|
12
|
+
if (fromEnv) {
|
|
13
|
+
return fromEnv;
|
|
14
|
+
}
|
|
15
|
+
const workspaceRoot = findWorkspaceRootSync();
|
|
16
|
+
if (!workspaceRoot) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return path.join(workspaceRoot, "packages", "agent", "dist", "index.js");
|
|
20
|
+
}
|
|
21
|
+
export function resolveStreamWorkingDirectory() {
|
|
22
|
+
return process.env.SALLY_STREAM_WORKDIR ?? findWorkspaceRootSync() ?? process.cwd();
|
|
23
|
+
}
|
|
24
|
+
export async function isProcessAlive(pid) {
|
|
25
|
+
try {
|
|
26
|
+
process.kill(pid, 0);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function ensurePathExists(targetPath) {
|
|
34
|
+
try {
|
|
35
|
+
await access(targetPath);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function sleep(durationMs) {
|
|
43
|
+
return new Promise((resolve) => setTimeout(resolve, durationMs));
|
|
44
|
+
}
|
|
45
|
+
export function spawnDetached(command, args, options) {
|
|
46
|
+
const child = spawn(command, args, {
|
|
47
|
+
...options,
|
|
48
|
+
detached: true,
|
|
49
|
+
stdio: "ignore"
|
|
50
|
+
});
|
|
51
|
+
child.unref();
|
|
52
|
+
return child;
|
|
53
|
+
}
|
|
54
|
+
function findWorkspaceRootSync() {
|
|
55
|
+
const candidates = [
|
|
56
|
+
process.cwd(),
|
|
57
|
+
path.dirname(fileURLToPath(import.meta.url))
|
|
58
|
+
];
|
|
59
|
+
for (const candidate of candidates) {
|
|
60
|
+
const root = walkUp(candidate);
|
|
61
|
+
if (root) {
|
|
62
|
+
return root;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function walkUp(startDirectory) {
|
|
68
|
+
let current = path.resolve(startDirectory);
|
|
69
|
+
while (true) {
|
|
70
|
+
const marker = path.join(current, "pnpm-workspace.yaml");
|
|
71
|
+
if (existsSync(marker)) {
|
|
72
|
+
return current;
|
|
73
|
+
}
|
|
74
|
+
const parent = path.dirname(current);
|
|
75
|
+
if (parent === current) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
current = parent;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function getOpenCommand(url) {
|
|
82
|
+
if (process.platform === "darwin") {
|
|
83
|
+
return { args: [url], command: "open" };
|
|
84
|
+
}
|
|
85
|
+
if (process.platform === "win32") {
|
|
86
|
+
return { args: ["/c", "start", "", url], command: "cmd" };
|
|
87
|
+
}
|
|
88
|
+
return { args: [url], command: "xdg-open" };
|
|
89
|
+
}
|
|
90
|
+
function spawnAndWait(command, args, options) {
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const child = spawn(command, args, options);
|
|
93
|
+
child.once("error", reject);
|
|
94
|
+
child.once("exit", (code) => {
|
|
95
|
+
if (code === 0) {
|
|
96
|
+
resolve();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
reject(new Error(`Command ${command} exited with code ${code ?? "unknown"}`));
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/utils/runtime.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heysalad/sally",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "HeySalad Sally command line interface.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"homepage": "https://github.com/Hey-Salad/sally#readme",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/Hey-Salad/sally/issues"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/Hey-Salad/sally.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"heysalad",
|
|
17
|
+
"sally",
|
|
18
|
+
"cli",
|
|
19
|
+
"cloudflare",
|
|
20
|
+
"device-automation",
|
|
21
|
+
"ios",
|
|
22
|
+
"android"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22.0.0"
|
|
26
|
+
},
|
|
27
|
+
"preferGlobal": true,
|
|
28
|
+
"bin": {
|
|
29
|
+
"sally": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"main": "./dist/index.js",
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"chalk": "^5.4.1",
|
|
47
|
+
"commander": "^13.1.0",
|
|
48
|
+
"ora": "^8.2.0",
|
|
49
|
+
"@heysalad/sally-sdk": "0.1.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^24.5.2",
|
|
53
|
+
"tsx": "^4.20.6",
|
|
54
|
+
"vitest": "^3.2.4"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc -p tsconfig.json",
|
|
58
|
+
"dev": "tsx watch src/index.ts",
|
|
59
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
60
|
+
"test": "vitest run --passWithNoTests",
|
|
61
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
62
|
+
}
|
|
63
|
+
}
|