@getdial/cli 0.33.1 → 0.33.2
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/dist/cli.js +1 -3
- package/dist/commands/call/list.js +5 -1
- package/dist/commands/listen/index.js +17 -3
- package/dist/commands/listen/install.js +7 -2
- package/dist/commands/listen/status.js +11 -7
- package/dist/commands/message/list.js +5 -1
- package/dist/commands/message/reply.js +5 -1
- package/dist/commands/number/set.js +6 -2
- package/dist/commands/onboard.js +17 -5
- package/dist/commands/signup.js +7 -1
- package/dist/commands/typing/start.js +5 -1
- package/dist/commands/typing/stop.js +5 -1
- package/dist/commands/uninstall.js +3 -1
- package/dist/commands/wait-for.js +2 -2
- package/dist/lib/api.js +13 -4
- package/dist/lib/cli-error.js +6 -1
- package/dist/lib/fanout.js +5 -3
- package/dist/lib/log-tail.js +5 -2
- package/dist/lib/log.js +2 -2
- package/dist/lib/ops/account.js +20 -7
- package/dist/lib/ops/calls.js +3 -1
- package/dist/lib/ops/events.js +6 -1
- package/dist/lib/ops/listen.js +8 -1
- package/dist/lib/ops/uninstall.js +9 -3
- package/dist/lib/pubnub.js +47 -9
- package/dist/lib/skill-install.js +1 -1
- package/dist/lib/supervisor/index.js +6 -3
- package/dist/lib/supervisor/launchd.js +13 -4
- package/dist/lib/supervisor/systemd.js +3 -1
- package/dist/lib/versioned-file.js +1 -1
- package/dist/mcp/schemas.js +22 -5
- package/dist/mcp/server.js +1 -3
- package/dist/mcp/tools/add-command-target.js +14 -3
- package/dist/mcp/tools/add-url-target.js +22 -5
- package/dist/mcp/tools/get-account-status.js +3 -1
- package/dist/mcp/tools/onboard.js +26 -7
- package/dist/mcp/tools/place-call.js +34 -8
- package/dist/mcp/tools/purchase-number.js +21 -5
- package/dist/mcp/tools/reply-to-message.js +4 -1
- package/dist/mcp/tools/send-message.js +8 -2
- package/dist/mcp/tools/set-number-properties.js +34 -8
- package/dist/mcp/tools/wait-for-event.js +12 -3
- package/package.json +5 -1
- package/skills.tar.gz +0 -0
package/dist/cli.js
CHANGED
|
@@ -99,9 +99,7 @@ if (!sandbox) {
|
|
|
99
99
|
.option("--json", "machine-readable output")
|
|
100
100
|
.action(async (opts) => process.exit(await runListenStatus({ json: !!opts.json })));
|
|
101
101
|
}
|
|
102
|
-
const number = program
|
|
103
|
-
.command("number")
|
|
104
|
-
.description("Manage your Dial phone numbers.");
|
|
102
|
+
const number = program.command("number").description("Manage your Dial phone numbers.");
|
|
105
103
|
number
|
|
106
104
|
.command("list")
|
|
107
105
|
.description("List the numbers on your account. GET /api/v1/numbers.")
|
|
@@ -3,7 +3,11 @@ import { isDialError } from "../../lib/ops/errors.js";
|
|
|
3
3
|
import { printDialError } from "../../lib/cli-error.js";
|
|
4
4
|
export async function runCallList(opts) {
|
|
5
5
|
try {
|
|
6
|
-
const calls = await listCalls({
|
|
6
|
+
const calls = await listCalls({
|
|
7
|
+
numberId: opts.numberId,
|
|
8
|
+
direction: opts.direction,
|
|
9
|
+
since: opts.since,
|
|
10
|
+
});
|
|
7
11
|
if (opts.json) {
|
|
8
12
|
console.log(JSON.stringify({ ok: true, calls }));
|
|
9
13
|
return 0;
|
|
@@ -11,17 +11,31 @@ function isSupervised() {
|
|
|
11
11
|
export async function runListen() {
|
|
12
12
|
const auth = readAuth();
|
|
13
13
|
if (!auth) {
|
|
14
|
-
appendJsonl(paths().listenLog, {
|
|
14
|
+
appendJsonl(paths().listenLog, {
|
|
15
|
+
ts: new Date().toISOString(),
|
|
16
|
+
lifecycle: "startup",
|
|
17
|
+
ok: false,
|
|
18
|
+
error: "no saved auth",
|
|
19
|
+
});
|
|
15
20
|
if (isSupervised()) {
|
|
16
21
|
await delay(30_000);
|
|
17
22
|
}
|
|
18
23
|
return 1;
|
|
19
24
|
}
|
|
20
|
-
appendJsonl(paths().listenLog, {
|
|
25
|
+
appendJsonl(paths().listenLog, {
|
|
26
|
+
ts: new Date().toISOString(),
|
|
27
|
+
lifecycle: "startup",
|
|
28
|
+
ok: true,
|
|
29
|
+
accountId: auth.accountId,
|
|
30
|
+
});
|
|
21
31
|
recordListenVersion();
|
|
22
32
|
const ctrl = startWorker(auth.apiKey, auth.accountId);
|
|
23
33
|
const onSignal = async (sig) => {
|
|
24
|
-
appendJsonl(paths().listenLog, {
|
|
34
|
+
appendJsonl(paths().listenLog, {
|
|
35
|
+
ts: new Date().toISOString(),
|
|
36
|
+
lifecycle: "shutdown",
|
|
37
|
+
signal: sig,
|
|
38
|
+
});
|
|
25
39
|
await ctrl.stop();
|
|
26
40
|
};
|
|
27
41
|
process.on("SIGTERM", onSignal);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readAuth } from "../../lib/state.js";
|
|
2
|
-
import { installSupervised, resolveListenCommand, supervisorAvailability } from "../../lib/supervisor/index.js";
|
|
2
|
+
import { installSupervised, resolveListenCommand, supervisorAvailability, } from "../../lib/supervisor/index.js";
|
|
3
3
|
export async function runListenInstall(opts) {
|
|
4
4
|
const auth = readAuth();
|
|
5
5
|
if (!auth) {
|
|
@@ -20,7 +20,12 @@ export async function runListenInstall(opts) {
|
|
|
20
20
|
try {
|
|
21
21
|
const result = installSupervised(resolveListenCommand());
|
|
22
22
|
if (opts.json)
|
|
23
|
-
console.log(JSON.stringify({
|
|
23
|
+
console.log(JSON.stringify({
|
|
24
|
+
ok: true,
|
|
25
|
+
changed: result.changed,
|
|
26
|
+
unit_path: result.unitPath,
|
|
27
|
+
warnings: result.warnings,
|
|
28
|
+
}));
|
|
24
29
|
else {
|
|
25
30
|
console.log(`listen service installed${result.changed ? "" : " (no change)"}.`);
|
|
26
31
|
console.log(` unit: ${result.unitPath}`);
|
|
@@ -8,14 +8,18 @@ export async function runListenStatus(opts) {
|
|
|
8
8
|
try {
|
|
9
9
|
const raw = readFileSync(paths().listenLog, "utf8");
|
|
10
10
|
const lines = raw.trim().split("\n").filter(Boolean);
|
|
11
|
-
lastEvents = lines.slice(-5).map((l) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
lastEvents = lines.slice(-5).map((l) => {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(l);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return l;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
/* ignore */
|
|
17
22
|
}
|
|
18
|
-
catch { /* ignore */ }
|
|
19
23
|
const out = {
|
|
20
24
|
installed: s.installed,
|
|
21
25
|
running: s.running,
|
|
@@ -3,7 +3,11 @@ import { isDialError } from "../../lib/ops/errors.js";
|
|
|
3
3
|
import { printDialError } from "../../lib/cli-error.js";
|
|
4
4
|
export async function runMessageList(opts) {
|
|
5
5
|
try {
|
|
6
|
-
const messages = await listMessages({
|
|
6
|
+
const messages = await listMessages({
|
|
7
|
+
numberId: opts.numberId,
|
|
8
|
+
direction: opts.direction,
|
|
9
|
+
since: opts.since,
|
|
10
|
+
});
|
|
7
11
|
if (opts.json) {
|
|
8
12
|
console.log(JSON.stringify({ ok: true, messages }));
|
|
9
13
|
return 0;
|
|
@@ -3,7 +3,11 @@ import { isDialError } from "../../lib/ops/errors.js";
|
|
|
3
3
|
import { printDialError } from "../../lib/cli-error.js";
|
|
4
4
|
export async function runMessageReply(opts) {
|
|
5
5
|
try {
|
|
6
|
-
const m = await replyToMessage({
|
|
6
|
+
const m = await replyToMessage({
|
|
7
|
+
messageId: opts.messageId,
|
|
8
|
+
body: opts.body,
|
|
9
|
+
reaction: opts.react,
|
|
10
|
+
});
|
|
7
11
|
if (opts.json) {
|
|
8
12
|
console.log(JSON.stringify({ ok: true, message: m }));
|
|
9
13
|
}
|
|
@@ -6,10 +6,14 @@ export async function runNumberSet(opts) {
|
|
|
6
6
|
const n = await setNumberProperties({
|
|
7
7
|
number: opts.number,
|
|
8
8
|
inboundInstruction: opts.inboundInstruction,
|
|
9
|
-
...(opts.inboundVoiceGender !== undefined
|
|
9
|
+
...(opts.inboundVoiceGender !== undefined
|
|
10
|
+
? { inboundVoiceGender: opts.inboundVoiceGender }
|
|
11
|
+
: {}),
|
|
10
12
|
...(opts.inboundLanguage !== undefined ? { inboundLanguage: opts.inboundLanguage } : {}),
|
|
11
13
|
...(opts.nickname !== undefined ? { nickname: opts.nickname } : {}),
|
|
12
|
-
...(opts.maxCallDurationSeconds !== undefined
|
|
14
|
+
...(opts.maxCallDurationSeconds !== undefined
|
|
15
|
+
? { maxCallDurationSeconds: opts.maxCallDurationSeconds }
|
|
16
|
+
: {}),
|
|
13
17
|
});
|
|
14
18
|
if (opts.json) {
|
|
15
19
|
console.log(JSON.stringify({ ok: true, number: n }));
|
package/dist/commands/onboard.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { onboard } from "../lib/ops/account.js";
|
|
2
2
|
import { isDialError } from "../lib/ops/errors.js";
|
|
3
3
|
import { readAuth, authFilePath } from "../lib/state.js";
|
|
4
|
-
import { installSkill, isSupportedAgent, SUPPORTED_AGENTS } from "../lib/skill-install.js";
|
|
4
|
+
import { installSkill, isSupportedAgent, SUPPORTED_AGENTS, } from "../lib/skill-install.js";
|
|
5
5
|
import { supervisorAvailability } from "../lib/supervisor/index.js";
|
|
6
6
|
function maskApiKey(key) {
|
|
7
7
|
return key.length >= 4 ? `sk_live_***${key.slice(-4)}` : "sk_live_***";
|
|
@@ -13,7 +13,10 @@ function installAgentSkills(agents) {
|
|
|
13
13
|
const skills = [];
|
|
14
14
|
for (const requested of agents) {
|
|
15
15
|
if (!isSupportedAgent(requested)) {
|
|
16
|
-
skills.push({
|
|
16
|
+
skills.push({
|
|
17
|
+
agent: requested,
|
|
18
|
+
error: `unknown agent "${requested}". Supported: ${SUPPORTED_AGENTS.join(", ")}.`,
|
|
19
|
+
});
|
|
17
20
|
continue;
|
|
18
21
|
}
|
|
19
22
|
try {
|
|
@@ -30,7 +33,7 @@ const LISTEN_RATIONALE = "The background notification listening service runs on
|
|
|
30
33
|
"Without it the agent is send-only: it can place outbound calls and send messages, but it has no way to know when something arrives at your Dial number. " +
|
|
31
34
|
"It can always be installed later with `dial listen install`.";
|
|
32
35
|
const PRESENTATION_HINT = "When you ask the user, describe it as \"the background notification listening service.\" Don't expose technical details (launchd, plists, daemons, systemd units) — that's not how to talk about it to a non-technical user.";
|
|
33
|
-
const FINALIZATION_HINT =
|
|
36
|
+
const FINALIZATION_HINT = 'To finalize onboarding, ask the user this question before continuing with any other task. If the user\'s original request was something else (e.g. "call X", "send Y"), briefly pause that task, ask the question, get an answer, then resume.';
|
|
34
37
|
export async function runOnboard(opts) {
|
|
35
38
|
// Skill-install-only branch: when --code isn't supplied, we can't (and shouldn't)
|
|
36
39
|
// re-verify — but if the machine is already signed in, the useful thing to do
|
|
@@ -59,9 +62,18 @@ export async function runOnboard(opts) {
|
|
|
59
62
|
accountId: auth.accountId,
|
|
60
63
|
phoneNumber: auth.phoneNumber ?? null,
|
|
61
64
|
phoneNumberId: auth.phoneNumberId ?? null,
|
|
62
|
-
listen: {
|
|
65
|
+
listen: {
|
|
66
|
+
installed: false,
|
|
67
|
+
autoInstalled: false,
|
|
68
|
+
canInstall: supervisor.available,
|
|
69
|
+
unavailableReason: supervisor.available ? null : supervisor.reason,
|
|
70
|
+
},
|
|
63
71
|
skills,
|
|
64
|
-
agentHint: {
|
|
72
|
+
agentHint: {
|
|
73
|
+
action: "skip",
|
|
74
|
+
kind: "already_signed_in",
|
|
75
|
+
note: "Account is already signed in; verification was skipped and only the requested --agent skills were installed.",
|
|
76
|
+
},
|
|
65
77
|
}));
|
|
66
78
|
}
|
|
67
79
|
else {
|
package/dist/commands/signup.js
CHANGED
|
@@ -18,7 +18,13 @@ export async function runSignup(email, opts) {
|
|
|
18
18
|
if (e.code === "pending_exists") {
|
|
19
19
|
const d = e.data ?? {};
|
|
20
20
|
if (opts.json) {
|
|
21
|
-
console.log(JSON.stringify({
|
|
21
|
+
console.log(JSON.stringify({
|
|
22
|
+
ok: false,
|
|
23
|
+
code: "pending_exists",
|
|
24
|
+
verificationId: d.verificationId,
|
|
25
|
+
email: d.email,
|
|
26
|
+
ageSeconds: d.ageSeconds,
|
|
27
|
+
}));
|
|
22
28
|
}
|
|
23
29
|
else {
|
|
24
30
|
console.error(e.message);
|
|
@@ -3,7 +3,11 @@ import { isDialError } from "../../lib/ops/errors.js";
|
|
|
3
3
|
import { printDialError } from "../../lib/cli-error.js";
|
|
4
4
|
export async function runTypingStart(opts) {
|
|
5
5
|
try {
|
|
6
|
-
const result = await setTyping({
|
|
6
|
+
const result = await setTyping({
|
|
7
|
+
toNumber: opts.toNumber,
|
|
8
|
+
value: true,
|
|
9
|
+
fromNumber: opts.fromNumber,
|
|
10
|
+
});
|
|
7
11
|
if (opts.json) {
|
|
8
12
|
console.log(JSON.stringify(result));
|
|
9
13
|
}
|
|
@@ -3,7 +3,11 @@ import { isDialError } from "../../lib/ops/errors.js";
|
|
|
3
3
|
import { printDialError } from "../../lib/cli-error.js";
|
|
4
4
|
export async function runTypingStop(opts) {
|
|
5
5
|
try {
|
|
6
|
-
const result = await setTyping({
|
|
6
|
+
const result = await setTyping({
|
|
7
|
+
toNumber: opts.toNumber,
|
|
8
|
+
value: false,
|
|
9
|
+
fromNumber: opts.fromNumber,
|
|
10
|
+
});
|
|
7
11
|
if (opts.json) {
|
|
8
12
|
console.log(JSON.stringify(result));
|
|
9
13
|
}
|
|
@@ -16,7 +16,9 @@ export async function runUninstall(opts) {
|
|
|
16
16
|
? `agent skills: removed from ${removedSkills.map((s) => s.agent).join(", ")}`
|
|
17
17
|
: "agent skills: none installed");
|
|
18
18
|
const removedDirs = report.dirs.filter((d) => d.removed);
|
|
19
|
-
console.log(removedDirs.length
|
|
19
|
+
console.log(removedDirs.length
|
|
20
|
+
? `state dirs: removed ${removedDirs.map((d) => d.path).join(", ")}`
|
|
21
|
+
: "state dirs: none present");
|
|
20
22
|
for (const e of report.errors) {
|
|
21
23
|
console.error(`error in ${e.step}: ${e.message}`);
|
|
22
24
|
}
|
|
@@ -19,7 +19,7 @@ export async function runWaitFor(opts) {
|
|
|
19
19
|
}
|
|
20
20
|
// Hit: print the raw line and succeed.
|
|
21
21
|
if (!r.timedOut && r.line != null) {
|
|
22
|
-
process.stdout.write(r.line
|
|
22
|
+
process.stdout.write(`${r.line}\n`);
|
|
23
23
|
return 0;
|
|
24
24
|
}
|
|
25
25
|
// Timed out tailing the log, but there was an earlier matching entry.
|
|
@@ -29,7 +29,7 @@ export async function runWaitFor(opts) {
|
|
|
29
29
|
}
|
|
30
30
|
else {
|
|
31
31
|
console.error(`timed out after ${opts.timeoutSeconds}s; latest matching entry in log:`);
|
|
32
|
-
process.stdout.write(r.line
|
|
32
|
+
process.stdout.write(`${r.line}\n`);
|
|
33
33
|
}
|
|
34
34
|
return 1;
|
|
35
35
|
}
|
package/dist/lib/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { request, fetch as undiciFetch, FormData as UndiciFormData, setGlobalDispatcher, EnvHttpProxyAgent } from "undici";
|
|
1
|
+
import { request, fetch as undiciFetch, FormData as UndiciFormData, setGlobalDispatcher, EnvHttpProxyAgent, } from "undici";
|
|
2
2
|
import { logger } from "./log.js";
|
|
3
3
|
import { VERSION } from "./version.js";
|
|
4
4
|
// Route this package's undici requests through HTTP(S)_PROXY when one is set.
|
|
@@ -8,7 +8,10 @@ import { VERSION } from "./version.js";
|
|
|
8
8
|
// OneCLI gateway sits on HTTPS_PROXY and injects the Authorization header for
|
|
9
9
|
// api.getdial.ai) our `request()`/`fetch()` calls would otherwise bypass the
|
|
10
10
|
// gateway entirely and go out UNAUTHENTICATED → 401. Opt in explicitly.
|
|
11
|
-
if (process.env.HTTPS_PROXY ||
|
|
11
|
+
if (process.env.HTTPS_PROXY ||
|
|
12
|
+
process.env.https_proxy ||
|
|
13
|
+
process.env.HTTP_PROXY ||
|
|
14
|
+
process.env.http_proxy) {
|
|
12
15
|
setGlobalDispatcher(new EnvHttpProxyAgent());
|
|
13
16
|
}
|
|
14
17
|
// The bundled undici only multipart-encodes its own FormData class (realm
|
|
@@ -35,7 +38,9 @@ function toResult(statusCode, text) {
|
|
|
35
38
|
try {
|
|
36
39
|
parsed = text ? JSON.parse(text) : null;
|
|
37
40
|
}
|
|
38
|
-
catch {
|
|
41
|
+
catch {
|
|
42
|
+
/* keep raw */
|
|
43
|
+
}
|
|
39
44
|
if (statusCode >= 200 && statusCode < 300) {
|
|
40
45
|
return { ok: true, status: statusCode, data: parsed };
|
|
41
46
|
}
|
|
@@ -52,7 +57,11 @@ function toResult(statusCode, text) {
|
|
|
52
57
|
}
|
|
53
58
|
async function apiRequest(method, path, body, apiKey, extraHeaders) {
|
|
54
59
|
const url = `${baseUrl()}${path}`;
|
|
55
|
-
const headers = {
|
|
60
|
+
const headers = {
|
|
61
|
+
"content-type": "application/json",
|
|
62
|
+
"user-agent": USER_AGENT,
|
|
63
|
+
...(extraHeaders ?? {}),
|
|
64
|
+
};
|
|
56
65
|
if (apiKey)
|
|
57
66
|
headers.authorization = `Bearer ${apiKey}`;
|
|
58
67
|
try {
|
package/dist/lib/cli-error.js
CHANGED
|
@@ -16,7 +16,12 @@ const EXIT_1_CODES = new Set([
|
|
|
16
16
|
*/
|
|
17
17
|
export function printDialError(json, e) {
|
|
18
18
|
if (json) {
|
|
19
|
-
console.log(JSON.stringify({
|
|
19
|
+
console.log(JSON.stringify({
|
|
20
|
+
ok: false,
|
|
21
|
+
code: e.code,
|
|
22
|
+
message: e.message,
|
|
23
|
+
...(e.status ? { status: e.status } : {}),
|
|
24
|
+
}));
|
|
20
25
|
}
|
|
21
26
|
else {
|
|
22
27
|
console.error(e.message);
|
package/dist/lib/fanout.js
CHANGED
|
@@ -7,12 +7,12 @@ function clipCapture(buf) {
|
|
|
7
7
|
const s = typeof buf === "string" ? buf : buf.toString("utf8");
|
|
8
8
|
if (s.length <= MAX_CAPTURE_BYTES)
|
|
9
9
|
return s;
|
|
10
|
-
return s.slice(0, MAX_CAPTURE_BYTES)
|
|
10
|
+
return `${s.slice(0, MAX_CAPTURE_BYTES)}…[truncated, total ${s.length} chars]`;
|
|
11
11
|
}
|
|
12
12
|
async function attemptUrl(target, body) {
|
|
13
13
|
const headers = { "Content-Type": "application/json" };
|
|
14
14
|
if (target.bearer)
|
|
15
|
-
headers
|
|
15
|
+
headers.Authorization = `Bearer ${target.bearer}`;
|
|
16
16
|
if (target.secret) {
|
|
17
17
|
const sig = createHmac("sha256", target.secret).update(body).digest("hex");
|
|
18
18
|
headers[target.signatureHeader ?? DEFAULT_SIGNATURE_HEADER] = sig;
|
|
@@ -65,7 +65,9 @@ async function attemptCmd(target, eventJson) {
|
|
|
65
65
|
try {
|
|
66
66
|
child.kill("SIGKILL");
|
|
67
67
|
}
|
|
68
|
-
catch {
|
|
68
|
+
catch {
|
|
69
|
+
/* already exited */
|
|
70
|
+
}
|
|
69
71
|
resolve({
|
|
70
72
|
ok: false,
|
|
71
73
|
timedOut: true,
|
package/dist/lib/log-tail.js
CHANGED
|
@@ -43,7 +43,7 @@ export function findLatestMatch(file, spec) {
|
|
|
43
43
|
return { line: lines[i], obj };
|
|
44
44
|
}
|
|
45
45
|
catch {
|
|
46
|
-
|
|
46
|
+
// skip unparsable lines
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
return null;
|
|
@@ -68,7 +68,10 @@ function readRange(file, offset, length) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
function parseLines(chunk) {
|
|
71
|
-
return chunk
|
|
71
|
+
return chunk
|
|
72
|
+
.split("\n")
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.map((line) => {
|
|
72
75
|
try {
|
|
73
76
|
return { line, obj: JSON.parse(line) };
|
|
74
77
|
}
|
package/dist/lib/log.js
CHANGED
|
@@ -3,7 +3,7 @@ import { dirname } from "node:path";
|
|
|
3
3
|
import pino from "pino";
|
|
4
4
|
export function appendJsonl(file, obj) {
|
|
5
5
|
mkdirSync(dirname(file), { recursive: true });
|
|
6
|
-
appendFileSync(file, JSON.stringify(obj)
|
|
6
|
+
appendFileSync(file, `${JSON.stringify(obj)}\n`);
|
|
7
7
|
}
|
|
8
8
|
export const logger = pino({
|
|
9
9
|
level: process.env.DIAL_LOG_LEVEL ?? "warn",
|
|
@@ -30,5 +30,5 @@ export function rotateIfLarge(file, maxBytes) {
|
|
|
30
30
|
const raw = readFileSync(file, "utf8");
|
|
31
31
|
const lines = raw.split("\n").filter(Boolean);
|
|
32
32
|
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
33
|
-
writeFileSync(file, keep.join("\n")
|
|
33
|
+
writeFileSync(file, `${keep.join("\n")}\n`);
|
|
34
34
|
}
|
package/dist/lib/ops/account.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { readAuth, readPendingSignup, writePendingSignup, clearPendingSignup, writeAuth, authFilePath } from "../state.js";
|
|
1
|
+
import { readAuth, readPendingSignup, writePendingSignup, clearPendingSignup, writeAuth, authFilePath, } from "../state.js";
|
|
2
2
|
import { apiGet, apiPost, baseUrl, pingBackend } from "../api.js";
|
|
3
|
-
import { supervisorStatus, lastEventAtFromLog, supervisorAvailability } from "../supervisor/index.js";
|
|
3
|
+
import { supervisorStatus, lastEventAtFromLog, supervisorAvailability, } from "../supervisor/index.js";
|
|
4
4
|
import { paths } from "../paths.js";
|
|
5
5
|
import { VERSION } from "../version.js";
|
|
6
|
-
import { installSkill, isSupportedAgent, SUPPORTED_AGENTS } from "../skill-install.js";
|
|
6
|
+
import { installSkill, isSupportedAgent, SUPPORTED_AGENTS, } from "../skill-install.js";
|
|
7
7
|
import { isSandbox } from "../sandbox.js";
|
|
8
8
|
import { DialError } from "./errors.js";
|
|
9
9
|
const OTP_EXPIRY_MS = 10 * 60 * 1000;
|
|
@@ -46,7 +46,11 @@ export async function accountStatus() {
|
|
|
46
46
|
let listenState = { installed: false, running: false, lastEventAt: null };
|
|
47
47
|
try {
|
|
48
48
|
const s = supervisorStatus();
|
|
49
|
-
listenState = {
|
|
49
|
+
listenState = {
|
|
50
|
+
installed: s.installed,
|
|
51
|
+
running: s.running,
|
|
52
|
+
lastEventAt: lastEventAtFromLog(paths().listenLog),
|
|
53
|
+
};
|
|
50
54
|
}
|
|
51
55
|
catch {
|
|
52
56
|
// unsupported platform — leave defaults
|
|
@@ -100,10 +104,16 @@ export async function signup(opts) {
|
|
|
100
104
|
throw new DialError("pending_exists", `A pending OTP for ${existing.email} is still fresh (${ageSeconds}s old). Use \`dial onboard --code <code>\` or re-run with --force to start a new one.`, undefined, { verificationId: existing.verificationId, email: existing.email, ageSeconds });
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
|
-
const res = await apiPost("/api/v1/auth/signup", {
|
|
107
|
+
const res = await apiPost("/api/v1/auth/signup", {
|
|
108
|
+
email: opts.email,
|
|
109
|
+
});
|
|
104
110
|
if (!res.ok)
|
|
105
111
|
throw new DialError("signup_failed", res.error, res.status);
|
|
106
|
-
writePendingSignup({
|
|
112
|
+
writePendingSignup({
|
|
113
|
+
verificationId: res.data.verificationId,
|
|
114
|
+
email: opts.email,
|
|
115
|
+
createdAt: new Date().toISOString(),
|
|
116
|
+
});
|
|
107
117
|
return { verificationId: res.data.verificationId, email: opts.email };
|
|
108
118
|
}
|
|
109
119
|
export async function onboard(opts) {
|
|
@@ -139,7 +149,10 @@ export async function onboard(opts) {
|
|
|
139
149
|
const skills = [];
|
|
140
150
|
for (const requested of opts.agents ?? []) {
|
|
141
151
|
if (!isSupportedAgent(requested)) {
|
|
142
|
-
skills.push({
|
|
152
|
+
skills.push({
|
|
153
|
+
agent: requested,
|
|
154
|
+
error: `unknown agent "${requested}". Supported: ${SUPPORTED_AGENTS.join(", ")}.`,
|
|
155
|
+
});
|
|
143
156
|
continue;
|
|
144
157
|
}
|
|
145
158
|
try {
|
package/dist/lib/ops/calls.js
CHANGED
|
@@ -12,7 +12,9 @@ export async function placeCall(opts) {
|
|
|
12
12
|
// Omitted → the server uses the default voice gender (female).
|
|
13
13
|
...(opts.voiceGender ? { voiceGender: opts.voiceGender } : {}),
|
|
14
14
|
...(opts.transferTo ? { transferTo: opts.transferTo } : {}),
|
|
15
|
-
...(opts.maxCallDurationSeconds !== undefined
|
|
15
|
+
...(opts.maxCallDurationSeconds !== undefined
|
|
16
|
+
? { maxCallDurationSeconds: opts.maxCallDurationSeconds }
|
|
17
|
+
: {}),
|
|
16
18
|
}, auth?.apiKey, opts.idempotencyKey ? { "idempotency-key": opts.idempotencyKey } : undefined);
|
|
17
19
|
if (!res.ok)
|
|
18
20
|
throw new DialError("call_failed", res.error, res.status);
|
package/dist/lib/ops/events.js
CHANGED
|
@@ -52,7 +52,12 @@ async function waitFromApi(spec, opts) {
|
|
|
52
52
|
timeout,
|
|
53
53
|
}, auth?.apiKey);
|
|
54
54
|
if (res.ok && res.data?.event) {
|
|
55
|
-
return {
|
|
55
|
+
return {
|
|
56
|
+
source: "api",
|
|
57
|
+
timedOut: false,
|
|
58
|
+
event: res.data.event,
|
|
59
|
+
line: JSON.stringify(res.data.event),
|
|
60
|
+
};
|
|
56
61
|
}
|
|
57
62
|
if (res.ok === false && res.status === 408)
|
|
58
63
|
continue;
|
package/dist/lib/ops/listen.js
CHANGED
|
@@ -44,5 +44,12 @@ export function listenStatus() {
|
|
|
44
44
|
catch {
|
|
45
45
|
// no log yet — leave empty
|
|
46
46
|
}
|
|
47
|
-
return {
|
|
47
|
+
return {
|
|
48
|
+
installed: s.installed,
|
|
49
|
+
running: s.running,
|
|
50
|
+
pid: s.pid,
|
|
51
|
+
unitPath: s.unitPath,
|
|
52
|
+
lastEventAt,
|
|
53
|
+
lastEvents,
|
|
54
|
+
};
|
|
48
55
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, rmSync } from "node:fs";
|
|
2
2
|
import { paths } from "../paths.js";
|
|
3
3
|
import { SUPPORTED_AGENTS, uninstallSkill } from "../skill-install.js";
|
|
4
|
-
import { supervisorAvailability, uninstallSupervised } from "../supervisor/index.js";
|
|
4
|
+
import { supervisorAvailability, uninstallSupervised, } from "../supervisor/index.js";
|
|
5
5
|
export const UNINSTALL_HINT = "npm uninstall -g @getdial/cli";
|
|
6
6
|
/**
|
|
7
7
|
* Full local teardown, best-effort: every step runs even if an earlier one
|
|
@@ -33,7 +33,10 @@ export function uninstallEverything(deps = {}) {
|
|
|
33
33
|
skills.push(uninstallSkill(agent, { home: deps.home, cwd: deps.cwd }));
|
|
34
34
|
}
|
|
35
35
|
catch (err) {
|
|
36
|
-
errors.push({
|
|
36
|
+
errors.push({
|
|
37
|
+
step: `skill:${agent}`,
|
|
38
|
+
message: err instanceof Error ? err.message : String(err),
|
|
39
|
+
});
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
const p = paths();
|
|
@@ -47,7 +50,10 @@ export function uninstallEverything(deps = {}) {
|
|
|
47
50
|
}
|
|
48
51
|
catch (err) {
|
|
49
52
|
dirs.push({ path: dir, removed: false });
|
|
50
|
-
errors.push({
|
|
53
|
+
errors.push({
|
|
54
|
+
step: `dir:${dir}`,
|
|
55
|
+
message: err instanceof Error ? err.message : String(err),
|
|
56
|
+
});
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
return { ok: errors.length === 0, daemon, skills, dirs, hint: UNINSTALL_HINT, errors };
|
package/dist/lib/pubnub.js
CHANGED
|
@@ -19,7 +19,9 @@ export function startWorker(apiKey, accountId) {
|
|
|
19
19
|
let stopped = false;
|
|
20
20
|
let consecutiveFailures = 0;
|
|
21
21
|
let resolveStopped;
|
|
22
|
-
const whenStopped = new Promise((r) => {
|
|
22
|
+
const whenStopped = new Promise((r) => {
|
|
23
|
+
resolveStopped = r;
|
|
24
|
+
});
|
|
23
25
|
function logLine(obj) {
|
|
24
26
|
rotateIfLarge(logFile, MAX_LOG_BYTES);
|
|
25
27
|
appendJsonl(logFile, obj);
|
|
@@ -34,14 +36,24 @@ export function startWorker(apiKey, accountId) {
|
|
|
34
36
|
}
|
|
35
37
|
catch (err) {
|
|
36
38
|
consecutiveFailures += 1;
|
|
37
|
-
logLine({
|
|
39
|
+
logLine({
|
|
40
|
+
ts: new Date().toISOString(),
|
|
41
|
+
lifecycle: "token_refresh",
|
|
42
|
+
ok: false,
|
|
43
|
+
error: err instanceof Error ? err.message : String(err),
|
|
44
|
+
consecutive_failures: consecutiveFailures,
|
|
45
|
+
});
|
|
38
46
|
if (consecutiveFailures >= REFRESH_FAILURES_BEFORE_EXIT) {
|
|
39
|
-
logLine({
|
|
47
|
+
logLine({
|
|
48
|
+
ts: new Date().toISOString(),
|
|
49
|
+
lifecycle: "shutdown",
|
|
50
|
+
reason: "refresh_failures_exceeded",
|
|
51
|
+
});
|
|
40
52
|
await stop();
|
|
41
53
|
process.exitCode = 1;
|
|
42
54
|
return;
|
|
43
55
|
}
|
|
44
|
-
const backoff = Math.min(60,
|
|
56
|
+
const backoff = Math.min(60, 2 ** consecutiveFailures) * 1000;
|
|
45
57
|
refreshTimer = setTimeout(() => refresh(creds), backoff);
|
|
46
58
|
}
|
|
47
59
|
}
|
|
@@ -59,13 +71,25 @@ export function startWorker(apiKey, accountId) {
|
|
|
59
71
|
pn?.unsubscribeAll();
|
|
60
72
|
}
|
|
61
73
|
catch (err) {
|
|
62
|
-
logLine({
|
|
74
|
+
logLine({
|
|
75
|
+
ts: new Date().toISOString(),
|
|
76
|
+
lifecycle: "shutdown_error",
|
|
77
|
+
phase: "unsubscribeAll",
|
|
78
|
+
error: err instanceof Error ? err.message : String(err),
|
|
79
|
+
stack: err instanceof Error ? err.stack : null,
|
|
80
|
+
});
|
|
63
81
|
}
|
|
64
82
|
try {
|
|
65
83
|
pn?.destroy?.();
|
|
66
84
|
}
|
|
67
85
|
catch (err) {
|
|
68
|
-
logLine({
|
|
86
|
+
logLine({
|
|
87
|
+
ts: new Date().toISOString(),
|
|
88
|
+
lifecycle: "shutdown_error",
|
|
89
|
+
phase: "destroy",
|
|
90
|
+
error: err instanceof Error ? err.message : String(err),
|
|
91
|
+
stack: err instanceof Error ? err.stack : null,
|
|
92
|
+
});
|
|
69
93
|
}
|
|
70
94
|
resolveStopped();
|
|
71
95
|
return whenStopped;
|
|
@@ -76,7 +100,12 @@ export function startWorker(apiKey, accountId) {
|
|
|
76
100
|
creds = await fetchSubscribeCreds(apiKey);
|
|
77
101
|
}
|
|
78
102
|
catch (err) {
|
|
79
|
-
logLine({
|
|
103
|
+
logLine({
|
|
104
|
+
ts: new Date().toISOString(),
|
|
105
|
+
lifecycle: "startup",
|
|
106
|
+
ok: false,
|
|
107
|
+
error: err instanceof Error ? err.message : String(err),
|
|
108
|
+
});
|
|
80
109
|
process.exitCode = 1;
|
|
81
110
|
resolveStopped();
|
|
82
111
|
return;
|
|
@@ -94,11 +123,20 @@ export function startWorker(apiKey, accountId) {
|
|
|
94
123
|
message: (ev) => {
|
|
95
124
|
logLine({ ts: new Date().toISOString(), ...ev.message });
|
|
96
125
|
void fanout(ev.message, logLine).catch((err) => {
|
|
97
|
-
logLine({
|
|
126
|
+
logLine({
|
|
127
|
+
ts: new Date().toISOString(),
|
|
128
|
+
lifecycle: "fanout_error",
|
|
129
|
+
error: err instanceof Error ? err.message : String(err),
|
|
130
|
+
});
|
|
98
131
|
});
|
|
99
132
|
},
|
|
100
133
|
status: (s) => {
|
|
101
|
-
logLine({
|
|
134
|
+
logLine({
|
|
135
|
+
ts: new Date().toISOString(),
|
|
136
|
+
lifecycle: "status",
|
|
137
|
+
category: s.category,
|
|
138
|
+
operation: s.operation ?? null,
|
|
139
|
+
});
|
|
102
140
|
},
|
|
103
141
|
});
|
|
104
142
|
pn.subscribe({ channels: [creds.channel] });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { homedir, tmpdir } from "node:os";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -3,8 +3,8 @@ import { userInfo } from "node:os";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { paths } from "../paths.js";
|
|
5
5
|
import { logger } from "../log.js";
|
|
6
|
-
import { LAUNCHD_LABEL, launchctlBootoutSilent, launchctlLoad, launchctlStatus, launchctlUnload, launchdPlistPath, renderLaunchdPlist, writeLaunchdPlist } from "./launchd.js";
|
|
7
|
-
import { lingerEnabled, renderSystemdUnit, systemctlDisable, systemctlEnableAndStart, systemctlStatus, systemdUnitPath, writeSystemdUnit } from "./systemd.js";
|
|
6
|
+
import { LAUNCHD_LABEL, launchctlBootoutSilent, launchctlLoad, launchctlStatus, launchctlUnload, launchdPlistPath, renderLaunchdPlist, writeLaunchdPlist, } from "./launchd.js";
|
|
7
|
+
import { lingerEnabled, renderSystemdUnit, systemctlDisable, systemctlEnableAndStart, systemctlStatus, systemdUnitPath, writeSystemdUnit, } from "./systemd.js";
|
|
8
8
|
export function currentPlatform() {
|
|
9
9
|
if (process.platform === "darwin")
|
|
10
10
|
return "darwin";
|
|
@@ -30,7 +30,10 @@ export function supervisorAvailability() {
|
|
|
30
30
|
return { available: false, reason: "XDG_RUNTIME_DIR is not set (no systemd user session)" };
|
|
31
31
|
}
|
|
32
32
|
if (!existsSync(`${runtimeDir}/systemd/private`)) {
|
|
33
|
-
return {
|
|
33
|
+
return {
|
|
34
|
+
available: false,
|
|
35
|
+
reason: "systemd user bus socket not found (sandbox or container without systemd --user)",
|
|
36
|
+
};
|
|
34
37
|
}
|
|
35
38
|
return { available: true };
|
|
36
39
|
}
|
|
@@ -35,7 +35,9 @@ export function renderLaunchdPlist(params) {
|
|
|
35
35
|
// so we must prepend the directory of the currently running node (e.g. nvm's bin dir)
|
|
36
36
|
// so the shebang can resolve. Falls back to /usr/local/bin which is where Homebrew puts node.
|
|
37
37
|
const nodeDir = dirname(process.execPath);
|
|
38
|
-
const programArguments = params.programArgs
|
|
38
|
+
const programArguments = params.programArgs
|
|
39
|
+
.map((arg) => ` <string>${arg}</string>`)
|
|
40
|
+
.join("\n");
|
|
39
41
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
40
42
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
41
43
|
<plist version="1.0">
|
|
@@ -130,13 +132,20 @@ export function launchctlUnload(plistPath) {
|
|
|
130
132
|
}
|
|
131
133
|
export function launchctlStatus() {
|
|
132
134
|
try {
|
|
133
|
-
const out = execFileSync("launchctl", ["list"], {
|
|
134
|
-
|
|
135
|
+
const out = execFileSync("launchctl", ["list"], {
|
|
136
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
137
|
+
}).toString();
|
|
138
|
+
const line = out
|
|
139
|
+
.split("\n")
|
|
140
|
+
.find((l) => l.endsWith(`\t${LAUNCHD_LABEL}`) || l.endsWith(` ${LAUNCHD_LABEL}`));
|
|
135
141
|
if (!line)
|
|
136
142
|
return { running: false, pid: null };
|
|
137
143
|
const cols = line.split(/\s+/);
|
|
138
144
|
const pid = parseInt(cols[0], 10);
|
|
139
|
-
return {
|
|
145
|
+
return {
|
|
146
|
+
running: Number.isFinite(pid) && pid > 0,
|
|
147
|
+
pid: Number.isFinite(pid) && pid > 0 ? pid : null,
|
|
148
|
+
};
|
|
140
149
|
}
|
|
141
150
|
catch (err) {
|
|
142
151
|
logger.warn({ err: redactBuffers(err) }, "launchctl list failed");
|
|
@@ -84,7 +84,9 @@ export function systemctlStatus() {
|
|
|
84
84
|
}
|
|
85
85
|
export function lingerEnabled(user) {
|
|
86
86
|
try {
|
|
87
|
-
const out = execFileSync("loginctl", ["show-user", user, "--property=Linger"], {
|
|
87
|
+
const out = execFileSync("loginctl", ["show-user", user, "--property=Linger"], {
|
|
88
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
89
|
+
}).toString();
|
|
88
90
|
return /Linger=yes/.test(out);
|
|
89
91
|
}
|
|
90
92
|
catch (err) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chmodSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { logger } from "./log.js";
|
|
4
4
|
const CHMOD_UNSUPPORTED_CODES = new Set(["ENOTSUP", "EOPNOTSUPP", "EPERM"]);
|
package/dist/mcp/schemas.js
CHANGED
|
@@ -9,7 +9,10 @@ import { z } from "zod";
|
|
|
9
9
|
const callStatusObjectSchema = z
|
|
10
10
|
.object({
|
|
11
11
|
state: z.string().nullish().describe('Lifecycle state, e.g. "Terminated", "Registered"'),
|
|
12
|
-
terminationType: z
|
|
12
|
+
terminationType: z
|
|
13
|
+
.string()
|
|
14
|
+
.nullish()
|
|
15
|
+
.describe('How it ended, e.g. "completed", "no-answer" (null until the call ends)'),
|
|
13
16
|
label: z.string().nullish().describe('Human-readable status, e.g. "Completed"'),
|
|
14
17
|
cancelRequested: z.boolean().nullish(),
|
|
15
18
|
cancelPending: z.boolean().nullish(),
|
|
@@ -25,8 +28,16 @@ export const phoneNumberSchema = z
|
|
|
25
28
|
nickname: z.string().nullable().optional().describe("Human-readable label for the number"),
|
|
26
29
|
country: z.string().optional(),
|
|
27
30
|
inboundInstruction: z.string().nullable().optional(),
|
|
28
|
-
inboundVoiceGender: z
|
|
29
|
-
|
|
31
|
+
inboundVoiceGender: z
|
|
32
|
+
.string()
|
|
33
|
+
.nullable()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe('Voice gender for inbound calls ("male"/"female"); null → female (the default)'),
|
|
36
|
+
inboundLanguage: z
|
|
37
|
+
.string()
|
|
38
|
+
.nullable()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe("BCP-47 language tag inbound calls are pinned to; null → detected from the caller's country prefix per call"),
|
|
30
41
|
})
|
|
31
42
|
.passthrough();
|
|
32
43
|
export const messageSchema = z
|
|
@@ -39,8 +50,14 @@ export const messageSchema = z
|
|
|
39
50
|
direction: z.string().optional(),
|
|
40
51
|
status: statusSchema,
|
|
41
52
|
statusError: z.string().nullish().describe("Failure reason when status is undelivered/failed"),
|
|
42
|
-
replyToId: z
|
|
43
|
-
|
|
53
|
+
replyToId: z
|
|
54
|
+
.string()
|
|
55
|
+
.nullish()
|
|
56
|
+
.describe("Id of the message this one replies or reacts to; null for ordinary messages"),
|
|
57
|
+
reaction: z
|
|
58
|
+
.string()
|
|
59
|
+
.nullish()
|
|
60
|
+
.describe("The reaction this message carries (a reaction name or an emoji); null otherwise"),
|
|
44
61
|
createdAt: z.string().optional(),
|
|
45
62
|
})
|
|
46
63
|
.passthrough();
|
package/dist/mcp/server.js
CHANGED
|
@@ -9,9 +9,7 @@ const SERVER_INFO = {
|
|
|
9
9
|
title: "Dial",
|
|
10
10
|
version: VERSION,
|
|
11
11
|
websiteUrl: "https://getdial.ai",
|
|
12
|
-
icons: [
|
|
13
|
-
{ src: "https://getdial.ai/favicon.svg", mimeType: "image/svg+xml", sizes: ["any"] },
|
|
14
|
-
],
|
|
12
|
+
icons: [{ src: "https://getdial.ai/favicon.svg", mimeType: "image/svg+xml", sizes: ["any"] }],
|
|
15
13
|
};
|
|
16
14
|
export function buildServer() {
|
|
17
15
|
const server = new McpServer(SERVER_INFO);
|
|
@@ -2,9 +2,20 @@ import { z } from "zod";
|
|
|
2
2
|
import { jsonResult } from "../result.js";
|
|
3
3
|
import { addCommandTarget } from "../../lib/ops/local-targets.js";
|
|
4
4
|
const inputSchema = {
|
|
5
|
-
path: z
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
path: z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.describe("Absolute path to an executable the daemon spawns once per event"),
|
|
9
|
+
args: z
|
|
10
|
+
.array(z.string())
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Extra args; the event JSON is appended as the final positional arg"),
|
|
13
|
+
timeoutSeconds: z
|
|
14
|
+
.number()
|
|
15
|
+
.int()
|
|
16
|
+
.positive()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Per-attempt timeout (default 5)"),
|
|
8
19
|
};
|
|
9
20
|
export const addCommandTargetTool = {
|
|
10
21
|
name: "add_command_target",
|
|
@@ -2,11 +2,28 @@ import { z } from "zod";
|
|
|
2
2
|
import { jsonResult } from "../result.js";
|
|
3
3
|
import { addUrlTarget } from "../../lib/ops/local-targets.js";
|
|
4
4
|
const inputSchema = {
|
|
5
|
-
url: z
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
url: z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.describe("Loopback HTTP endpoint the listen daemon POSTs each event JSON to"),
|
|
9
|
+
secret: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("HMAC-SHA256 key; the daemon signs each request body and sends the hex digest"),
|
|
13
|
+
signatureHeader: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Header for the HMAC signature (default X-Dial-Signature; only with secret)"),
|
|
17
|
+
bearer: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Static bearer token, sent as Authorization: Bearer <token>"),
|
|
21
|
+
timeoutSeconds: z
|
|
22
|
+
.number()
|
|
23
|
+
.int()
|
|
24
|
+
.positive()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Per-attempt timeout (default 5)"),
|
|
10
27
|
};
|
|
11
28
|
export const addUrlTargetTool = {
|
|
12
29
|
name: "add_url_target",
|
|
@@ -14,7 +14,9 @@ export const getAccountStatusTool = {
|
|
|
14
14
|
auth: z.object({}).passthrough().describe("Sign-in and API-key state"),
|
|
15
15
|
pendingOtp: z.object({}).passthrough().describe("Any pending sign-up OTP"),
|
|
16
16
|
listen: z.object({}).passthrough().describe("Listen daemon state"),
|
|
17
|
-
nextStep: z
|
|
17
|
+
nextStep: z
|
|
18
|
+
.string()
|
|
19
|
+
.describe("Recommended next step (signup, onboard, install_listen, ready, …)"),
|
|
18
20
|
},
|
|
19
21
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
20
22
|
},
|
|
@@ -2,13 +2,26 @@ import { z } from "zod";
|
|
|
2
2
|
import { jsonResult } from "../result.js";
|
|
3
3
|
import { onboard } from "../../lib/ops/account.js";
|
|
4
4
|
import { readAuth, authFilePath } from "../../lib/state.js";
|
|
5
|
-
import { installSkill, isSupportedAgent, SUPPORTED_AGENTS } from "../../lib/skill-install.js";
|
|
5
|
+
import { installSkill, isSupportedAgent, SUPPORTED_AGENTS, } from "../../lib/skill-install.js";
|
|
6
6
|
import { supervisorAvailability } from "../../lib/supervisor/index.js";
|
|
7
7
|
const inputSchema = {
|
|
8
|
-
code: z
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
code: z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("6-digit OTP from the sign-up email. Omit if the account is already signed in — the tool will just install the requested --agent skills and skip verification."),
|
|
13
|
+
verificationId: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Explicit verification id (defaults to the local pending signup)"),
|
|
17
|
+
inboundInstruction: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("System prompt for inbound calls to a newly provisioned number (new accounts)"),
|
|
21
|
+
agents: z
|
|
22
|
+
.array(z.string())
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Agent names to install the Dial skill into (e.g. claude-code, cursor)"),
|
|
12
25
|
};
|
|
13
26
|
export const onboardTool = {
|
|
14
27
|
name: "onboard",
|
|
@@ -40,14 +53,20 @@ export const onboardTool = {
|
|
|
40
53
|
const skills = [];
|
|
41
54
|
for (const requested of args.agents ?? []) {
|
|
42
55
|
if (!isSupportedAgent(requested)) {
|
|
43
|
-
skills.push({
|
|
56
|
+
skills.push({
|
|
57
|
+
agent: requested,
|
|
58
|
+
error: `unknown agent "${requested}". Supported: ${SUPPORTED_AGENTS.join(", ")}.`,
|
|
59
|
+
});
|
|
44
60
|
continue;
|
|
45
61
|
}
|
|
46
62
|
try {
|
|
47
63
|
skills.push(installSkill(requested));
|
|
48
64
|
}
|
|
49
65
|
catch (err) {
|
|
50
|
-
skills.push({
|
|
66
|
+
skills.push({
|
|
67
|
+
agent: requested,
|
|
68
|
+
error: err instanceof Error ? err.message : String(err),
|
|
69
|
+
});
|
|
51
70
|
}
|
|
52
71
|
}
|
|
53
72
|
const supervisor = supervisorAvailability();
|
|
@@ -4,18 +4,41 @@ import { placeCall } from "../../lib/ops/calls.js";
|
|
|
4
4
|
import { callSchema } from "../schemas.js";
|
|
5
5
|
const inputSchema = {
|
|
6
6
|
to: z.string().min(7).describe("Destination phone number, E.164 (e.g. +14155550123)"),
|
|
7
|
-
outboundInstruction: z
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
outboundInstruction: z
|
|
8
|
+
.string()
|
|
9
|
+
.min(1)
|
|
10
|
+
.describe("System prompt for the AI voice agent on this call"),
|
|
11
|
+
language: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("BCP-47 language tag for the call. Omit to auto-detect from the destination number's country (alongside en-US)."),
|
|
15
|
+
voiceGender: z
|
|
16
|
+
.enum(["male", "female"])
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Voice gender for the agent; the default is female"),
|
|
19
|
+
transferTo: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Forward-to number, E.164: the agent waits for a real human (riding out hold/IVR) then cold-transfers the call here. Must differ from `to` and the from number."),
|
|
23
|
+
idempotencyKey: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Unique key (e.g. a UUID) making the placement idempotent: retrying with the same key returns the already-placed call instead of dialing again"),
|
|
12
27
|
fromNumber: z
|
|
13
28
|
.string()
|
|
14
29
|
.min(1)
|
|
15
30
|
.optional()
|
|
16
31
|
.describe("Number to call from: a phone number id, one of your numbers in E.164, or a nickname. Exclusive with fromNumberId; omit both to use your primary number"),
|
|
17
|
-
fromNumberId: z
|
|
18
|
-
|
|
32
|
+
fromNumberId: z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Number id to call from; defaults to your primary number"),
|
|
36
|
+
maxCallDurationSeconds: z
|
|
37
|
+
.number()
|
|
38
|
+
.int()
|
|
39
|
+
.positive()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Maximum call duration cap (seconds); the call is terminated when this limit is reached"),
|
|
19
42
|
};
|
|
20
43
|
export const placeCallTool = {
|
|
21
44
|
name: "place_call",
|
|
@@ -24,7 +47,10 @@ export const placeCallTool = {
|
|
|
24
47
|
description: "Place an outbound voice call handled by an AI agent. The call runs asynchronously — " +
|
|
25
48
|
"use wait_for_event to block until it ends, then get_call for the transcript.",
|
|
26
49
|
inputSchema,
|
|
27
|
-
outputSchema: {
|
|
50
|
+
outputSchema: {
|
|
51
|
+
call: callSchema,
|
|
52
|
+
hint: z.string().describe("Next-step guidance for tracking the call"),
|
|
53
|
+
},
|
|
28
54
|
annotations: { openWorldHint: true },
|
|
29
55
|
},
|
|
30
56
|
run: async (args) => {
|
|
@@ -4,11 +4,27 @@ import { purchaseNumber } from "../../lib/ops/numbers.js";
|
|
|
4
4
|
import { phoneNumberSchema } from "../schemas.js";
|
|
5
5
|
const inputSchema = {
|
|
6
6
|
inboundInstruction: z.string().min(1).describe("System prompt for inbound calls to this number"),
|
|
7
|
-
explicitProgrammaticConsent: z
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
explicitProgrammaticConsent: z
|
|
8
|
+
.string()
|
|
9
|
+
.min(1)
|
|
10
|
+
.max(2000)
|
|
11
|
+
.describe("Required attestation (max 2000 chars) that the account holder consented to provisioning this number programmatically; stored on the number"),
|
|
12
|
+
inboundVoiceGender: z
|
|
13
|
+
.enum(["male", "female"])
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Voice gender for inbound calls to this number; the default is female"),
|
|
16
|
+
inboundLanguage: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("BCP-47 language tag pinning inbound calls to this number to one language (e.g. es-ES); omitted → the language is detected from the caller's country prefix on each call (plus en-US)"),
|
|
20
|
+
areaCode: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Preferred US area code; omitted → any available US number. Only US numbers can be provisioned at this time. Ignored for iMessage numbers"),
|
|
24
|
+
includeImessage: z
|
|
25
|
+
.boolean()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Provision an iMessage number (pay-as-you-go only; provisioned asynchronously — poll List Numbers until setupStatus is "ready")'),
|
|
12
28
|
};
|
|
13
29
|
export const purchaseNumberTool = {
|
|
14
30
|
name: "purchase_number",
|
|
@@ -6,7 +6,10 @@ const inputSchema = {
|
|
|
6
6
|
messageId: z
|
|
7
7
|
.string()
|
|
8
8
|
.describe("Id of the message to reply or react to (from list_messages or a message.received event)"),
|
|
9
|
-
body: z
|
|
9
|
+
body: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Reply text; on an iMessage number it threads under the target message"),
|
|
10
13
|
reaction: z
|
|
11
14
|
.string()
|
|
12
15
|
.optional()
|
|
@@ -4,13 +4,19 @@ import { sendMessage, MAX_MEDIA_ITEMS } from "../../lib/ops/messages.js";
|
|
|
4
4
|
import { messageSchema } from "../schemas.js";
|
|
5
5
|
const inputSchema = {
|
|
6
6
|
to: z.string().min(7).describe("Destination phone number, E.164 (e.g. +14155550123)"),
|
|
7
|
-
body: z
|
|
7
|
+
body: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Message body; optional when mediaUrls is given (media-only send)"),
|
|
8
11
|
fromNumber: z
|
|
9
12
|
.string()
|
|
10
13
|
.min(1)
|
|
11
14
|
.optional()
|
|
12
15
|
.describe("Number to send from: a phone number id, one of your numbers in E.164, or a nickname. Exclusive with fromNumberId; omit both to use your primary number"),
|
|
13
|
-
fromNumberId: z
|
|
16
|
+
fromNumberId: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Number id to send from; defaults to your primary number"),
|
|
14
20
|
mediaUrls: z
|
|
15
21
|
.array(z.string().url())
|
|
16
22
|
.max(MAX_MEDIA_ITEMS)
|
|
@@ -4,11 +4,31 @@ import { setNumberProperties } from "../../lib/ops/numbers.js";
|
|
|
4
4
|
import { phoneNumberSchema } from "../schemas.js";
|
|
5
5
|
const inputSchema = {
|
|
6
6
|
number: z.string().min(7).describe("The E.164 phone number to update (e.g. +14155550123)"),
|
|
7
|
-
inboundInstruction: z
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
inboundInstruction: z
|
|
8
|
+
.string()
|
|
9
|
+
.min(1)
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("New system prompt for inbound calls to this number"),
|
|
12
|
+
inboundVoiceGender: z
|
|
13
|
+
.enum(["male", "female"])
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Voice gender for inbound calls to this number; the default is female"),
|
|
16
|
+
inboundLanguage: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("BCP-47 language tag pinning inbound calls to this number to one language (e.g. es-ES). Pass an empty string to clear it (reverts to detecting the language from the caller's country prefix per call)."),
|
|
20
|
+
nickname: z
|
|
21
|
+
.string()
|
|
22
|
+
.max(100)
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('Human-readable label for the number, e.g. "Support line". Pass an empty string to clear it.'),
|
|
25
|
+
maxCallDurationSeconds: z
|
|
26
|
+
.number()
|
|
27
|
+
.int()
|
|
28
|
+
.positive()
|
|
29
|
+
.nullable()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Call duration cap for this number, in seconds, applied as a hard ceiling to both inbound and outbound calls (the smallest of the per-number, account, and per-call caps wins). Pass null to clear the cap; omit to leave it unchanged."),
|
|
12
32
|
};
|
|
13
33
|
export const setNumberPropertiesTool = {
|
|
14
34
|
name: "set_number_properties",
|
|
@@ -23,10 +43,16 @@ export const setNumberPropertiesTool = {
|
|
|
23
43
|
number: await setNumberProperties({
|
|
24
44
|
number: args.number,
|
|
25
45
|
inboundInstruction: args.inboundInstruction,
|
|
26
|
-
...(args.inboundVoiceGender !== undefined
|
|
27
|
-
|
|
46
|
+
...(args.inboundVoiceGender !== undefined
|
|
47
|
+
? { inboundVoiceGender: args.inboundVoiceGender }
|
|
48
|
+
: {}),
|
|
49
|
+
...(args.inboundLanguage !== undefined
|
|
50
|
+
? { inboundLanguage: args.inboundLanguage }
|
|
51
|
+
: {}),
|
|
28
52
|
...(args.nickname !== undefined ? { nickname: args.nickname } : {}),
|
|
29
|
-
...(args.maxCallDurationSeconds !== undefined
|
|
53
|
+
...(args.maxCallDurationSeconds !== undefined
|
|
54
|
+
? { maxCallDurationSeconds: args.maxCallDurationSeconds }
|
|
55
|
+
: {}),
|
|
30
56
|
}),
|
|
31
57
|
}),
|
|
32
58
|
};
|
|
@@ -3,9 +3,18 @@ import { jsonResult } from "../result.js";
|
|
|
3
3
|
import { waitForEvent } from "../../lib/ops/events.js";
|
|
4
4
|
import { eventSchema } from "../schemas.js";
|
|
5
5
|
const inputSchema = {
|
|
6
|
-
eventType: z
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
eventType: z
|
|
7
|
+
.string()
|
|
8
|
+
.min(1)
|
|
9
|
+
.describe('Event type to wait for (e.g. "call.ended", "message.received")'),
|
|
10
|
+
field: z
|
|
11
|
+
.array(z.string())
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Exact-match filters, each "name=value" (e.g. "callId=abc")'),
|
|
14
|
+
regex: z
|
|
15
|
+
.array(z.string())
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Regex filters, each "name=pattern" (/re/flags or a bare regex)'),
|
|
9
18
|
timeoutSeconds: z.number().default(30).describe("How long to wait before giving up"),
|
|
10
19
|
};
|
|
11
20
|
export const waitForEventTool = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getdial/cli",
|
|
3
|
-
"version": "0.33.
|
|
3
|
+
"version": "0.33.2",
|
|
4
4
|
"description": "Dial CLI — install, sign up, and run the local listen service.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsc -p .",
|
|
29
29
|
"test": "node --import tsx --test",
|
|
30
|
+
"lint": "biome lint src",
|
|
31
|
+
"format": "biome format --write src",
|
|
32
|
+
"format:check": "biome format src",
|
|
30
33
|
"clean": "rm -rf dist skills.tar.gz",
|
|
31
34
|
"build:skill": "tar -czf skills.tar.gz skills",
|
|
32
35
|
"prepack": "npm run build && npm run build:skill"
|
|
@@ -41,6 +44,7 @@
|
|
|
41
44
|
"zod": "^4.4.3"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
47
|
+
"@biomejs/biome": "2.5.4",
|
|
44
48
|
"@types/node": "^22.10.0",
|
|
45
49
|
"tsx": "^4.22.3",
|
|
46
50
|
"typescript": "^5.6.0"
|
package/skills.tar.gz
CHANGED
|
Binary file
|