SparkSync Mobile

Hi everyone. I made an iphone app to manage my Sparks. Check my LinkedIn post:

This is great! I have been hoping for something that would run my commands to an agent/harness on my spark over tailscale and send me a notification when it’s done.

Any chance you are taking feature requests? :)

He does ;)

Thank you for this I think the APP looks great. Mobile DGX access is very useful when you are running long jobs and tailing logs. Thank you for developing a standalone app for DGXspark.

I was hoping someone was going to make an APP for DGX after I posted this.

https://forums.developer.nvidia.com/t/mobile-dgx-management-in-2026-dgx-console-on-iphone-jupyterlab-on-ipad-with-termius-tailscale/356609/2

One thing that would be useful is having the DGX console as a default app. I tried to add as a custom port and could not get it to work.

@christopher_owen

Thank you for all you great posts, they have been very useful, I really appreciate it. Here is a notification script.

Notification Script

Just edit your phone number for iMessage and create a Gmail App password and you are good to go. You will receive a nicely formatted iMessage and Email on completion of your script.

#!/usr/bin/env python3
# notify_complete.py
# Universal notification script - call this at the end of any script
# Sends both email AND iMessage on success or failure

import subprocess
import sys
import os
from datetime import datetime
from pathlib import Path
import argparse

# ==================== CONFIGURATION ====================
# Edit these values for your setup

# iMessage recipient (your iPhone number)
IPHONE_NUMBER = "+1XXXXXXXXXX"

# Email configuration
EMAIL_TO = "XXXXXXX@gmail.com"
EMAIL_FROM = "XXXXXX@gmail.com"
EMAIL_APP_PASSWORD = "XXXX XXXX XXXX XXXX"  # Gmail app password

# ==================== END CONFIGURATION ====================


def send_imessage(script_name: str, exit_code: int, duration: str = "", 
                  custom_message: str = "", log_file: str = ""):
    """Send iMessage notification"""
    
    # Determine status
    if exit_code == 0:
        status = "SUCCESS"
        emoji = "✅"
        title = "COMPLETED SUCCESSFULLY"
    else:
        status = "FAILED"
        emoji = "❌"
        title = f"FAILED (Exit Code: {exit_code})"
    
    # Build message
    current_time = datetime.now().strftime('%B %d, %Y • %I:%M %p')
    
    message = f"""{emoji} *SCRIPT {title}*

*Script:* {script_name}
*Time:* {current_time}
*Status:* {status}"""

    if duration:
        message += f"\n*Duration:* {duration}"
    
    if custom_message:
        message += f"\n\n{custom_message}"
    
    if log_file:
        message += f"\n\n📋 Log: {log_file}"
    
    message += "\n\n— Notification Bot"
    
    # Escape double quotes for AppleScript
    message = message.replace('"', '\\"')
    
    # AppleScript to send iMessage
    apple_script = f'''
    tell application "Messages"
        try
            set iMessageService to 1st service whose service type is iMessage
            set theBuddy to buddy "{IPHONE_NUMBER}" of iMessageService
            send "{message}" to theBuddy
            return "Success"
        on error errMsg
            return "Failed: " & errMsg
        end try
    end tell
    '''
    
    try:
        result = subprocess.run(
            ["osascript", "-e", apple_script],
            check=True,
            capture_output=True,
            text=True
        )
        print(f"📱 iMessage sent: {status}")
        return True
    except subprocess.CalledProcessError as e:
        error = e.stderr.strip() if e.stderr else "Unknown error"
        print(f"⚠️  Failed to send iMessage: {error}", file=sys.stderr)
        return False
    except Exception as e:
        print(f"⚠️  Unexpected iMessage error: {e}", file=sys.stderr)
        return False


def send_email(script_name: str, exit_code: int, duration: str = "",
               custom_message: str = "", log_file: str = ""):
    """Send email notification"""
    
    # Determine status
    if exit_code == 0:
        status = "SUCCESS"
        emoji = "✅"
        subject = f"✅ Script Success: {script_name}"
    else:
        status = "FAILED"
        emoji = "❌"
        subject = f"❌ Script Failed: {script_name} (Exit: {exit_code})"
    
    # Add timestamp to subject
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')
    full_subject = f"{subject} [{timestamp}]"
    
    # Build email body
    body = f"""Script Execution {status}

Script: {script_name}
Exit Code: {exit_code}
Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""

    if duration:
        body += f"Duration: {duration}\n"
    
    if custom_message:
        body += f"\nMessage:\n{custom_message}\n"
    
    if log_file:
        body += f"\nLog File: {log_file}\n"
    
    body += "\n— Notification Bot"
    
    # Send email via curl
    email_content = f"""From: {EMAIL_FROM}
To: {EMAIL_TO}
Subject: {full_subject}

{body}"""
    
    try:
        result = subprocess.run(
            [
                "curl", "--silent", "--show-error",
                "--url", "smtps://smtp.gmail.com:465",
                "--ssl-reqd",
                "--mail-from", EMAIL_FROM,
                "--mail-rcpt", EMAIL_TO,
                "--user", f"{EMAIL_FROM}:{EMAIL_APP_PASSWORD}",
                "--upload-file", "-"
            ],
            input=email_content,
            text=True,
            capture_output=True,
            check=True
        )
        print(f"📧 Email sent: {status}")
        return True
    except subprocess.CalledProcessError as e:
        print(f"⚠️  Failed to send email: {e.stderr}", file=sys.stderr)
        return False
    except Exception as e:
        print(f"⚠️  Unexpected email error: {e}", file=sys.stderr)
        return False


def format_duration(seconds: float) -> str:
    """Format duration in seconds to human-readable string"""
    if seconds < 60:
        return f"{seconds:.1f} seconds"
    elif seconds < 3600:
        minutes = seconds / 60
        return f"{minutes:.1f} minutes"
    else:
        hours = seconds / 3600
        return f"{hours:.1f} hours"


def main():
    parser = argparse.ArgumentParser(description="Send completion notifications via email and iMessage")
    parser.add_argument("exit_code", type=int, help="Exit code of the script (0 for success, non-zero for failure)")
    parser.add_argument("script_name", help="Name of the script that completed")
    parser.add_argument("--duration", type=float, help="Execution duration in seconds")
    parser.add_argument("--message", "-m", help="Custom message to include")
    parser.add_argument("--log", "-l", help="Path to log file")
    parser.add_argument("--no-email", action="store_true", help="Skip email notification")
    parser.add_argument("--no-imessage", action="store_true", help="Skip iMessage notification")
    
    args = parser.parse_args()
    
    # Format duration if provided
    duration_str = format_duration(args.duration) if args.duration else ""
    
    # Send notifications
    email_sent = False
    imessage_sent = False
    
    if not args.no_email:
        email_sent = send_email(
            args.script_name, 
            args.exit_code, 
            duration_str,
            args.message, 
            args.log
        )
    
    if not args.no_imessage:
        imessage_sent = send_imessage(
            args.script_name, 
            args.exit_code, 
            duration_str,
            args.message, 
            args.log
        )
    
    # Exit with success if at least one notification method worked
    if (args.no_email or email_sent) and (args.no_imessage or imessage_sent):
        sys.exit(0)
    else:
        sys.exit(1)


if __name__ == "__main__":
    main()

**
iMessage Notification
**

Email Notification

Thank you for testing the app.
Sure. I will do my best to be agile, but I am dealing with many projects and deadlines. Please post it through GitHub or email me. I’ll cure a to-do list an work on them.

Cool. I missed your post as the forum is huge and I don’t check it frequently. Adding DGX console would be amazing.

I’ve contacted a friend at NVIDIA to see if they’re interested in taking over the app and grow it. At the same time, I’ll expand it as much as I can. Developing the android version is a high priority as I have received multiple requests for it

Good idea contacting Nvidia. I think it would be great if Nvidia took over the App and created a suite of Apps for iPhone, Android, Mac and Windows.

There are very simple Apps that we need.

  1. Nvidia iCloud app it probably the first essential App.
    I do Borg Backups, Mac Backups and Wasabi but still trip over on permissions etc occasionally and get silent fails etc. For Nvidia this would be the start of a services business on DGXspark.

  2. A wrapper for setting up SMB drives on Mac and WIndows for drag drop functionality.

  3. Launcher App

I have created my own launcher app on Mac where I can just drop a file on an App in the dock and it executes on DGX.

There are countless apps to build that can reduce the necessity of CLI knowledge and expand the user base.

  1. It would be nice to get clarity from Nvidia on the roll out of NIM for DGXspark, this it probably the biggest thing for me.

Mark

Let’s talk Ernest. I think there’s some synergy with Spark Arena initiative. Now that Drew @dbsci is joining forces with @eugr and I to integrate his project https://sparkrun.dev into the other projects under the umbrella and provide a more consistent experience to Spark owners.
One use case you get for free would be importing new LLM recipe registries and executing those recipes from your app.

Thank you for your contribution ernest.namdar! I’m moving this thread to GB 10 Projects.