pulli/pullbox

Static methods for using AppleScript and DEVONthink

Installs: 321

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 0

Open Issues: 0

pkg:composer/pulli/pullbox

v2.0.1 2026-01-24 10:13 UTC

README

A PHP 8.4+ library providing static methods for executing AppleScript on macOS, with wrappers for system operations, Music app, dialogs, notifications, and DEVONthink.

release packagist license downloads tests

Note: This library requires macOS to execute AppleScript commands. The AppleScript class can generate scripts on any platform, but the facade classes (System, Music, Dialog, Notification, DEVONthink) require macOS.

Installation

composer require pulli/pullbox

Usage

Dialog

Display macOS dialogs with customizable buttons and optional text input.

use Pulli\Pullbox\Dialog;

// Simple dialog with OK button
$result = Dialog::display('Are you sure?', 'Confirmation');

// Dialog with custom buttons, default and cancel
$result = Dialog::display('Save changes?', 'Save', [
    'buttons' => ['Save', 'Discard', 'Cancel'],
    'defaultButton' => 'Save',
    'cancelButton' => 'Cancel',
]);

// Buttons referenced by position (1-indexed)
$result = Dialog::display('Continue?', 'Confirm', [
    'buttons' => ['Yes', 'No'],
    'defaultButton' => 1,
    'cancelButton' => 2,
]);

// Dialog with text input
$result = Dialog::display('Enter your name:', 'Input', [
    'answer' => true,
]);

// Pre-filled text input
$result = Dialog::display('Rename:', 'Edit', [
    'answer' => 'current name',
]);

// Password input (hidden)
$result = Dialog::display('Password:', 'Auth', [
    'answer' => true,
    'hiddenAnswer' => true,
]);

// With icon (stop, caution, or note)
$result = Dialog::display('Are you sure?', 'Warning', [
    'icon' => 'caution',
]);

// Auto-dismiss after N seconds
$result = Dialog::display('Closing soon...', 'Notice', [
    'givingUpAfter' => 10,
]);

Methods:

Method Parameters Returns
display string $message, ?string $title, array $options = [] ?string (button pressed or text input, null if cancelled)

Options array:

  • buttons — Array of button labels, max 3 (default: ['OK'])
  • defaultButton — Button name or 1-indexed position for the default button
  • cancelButton — Button name or 1-indexed position for the cancel button
  • answertrue for empty text field, or a string to pre-fill (default: false)
  • hiddenAnswertrue to mask input like a password field
  • icon — Dialog icon: 'stop', 'caution', or 'note'
  • givingUpAfter — Auto-dismiss the dialog after N seconds

Notification

Display macOS notifications with optional title, subtitle, and sound.

use Pulli\Pullbox\Notification;

// Simple notification
Notification::display('Download complete');

// With title
Notification::display('Download complete', 'My App');

// With subtitle
Notification::display('Download complete', 'My App', 'All files processed');

// With sound
Notification::display('Download complete', 'My App', null, 'Glass');

// All parameters
Notification::display('Download complete', 'My App', 'All files processed', 'Frog');

Methods:

Method Parameters Returns
display string $message, ?string $title = null, ?string $subtitle = null, ?string $soundName = null void

System

System-level operations for managing applications.

use Pulli\Pullbox\System;

// Get the Applications folder path
$path = System::applicationsFolder(); // e.g. "/Applications/"

// Move an app to Applications (quits it first, then relaunches)
System::moveApp('MyApp', '/tmp/MyApp.app');

// Move without relaunching
System::moveApp('MyApp', '/tmp/MyApp.app', launch: false);

// Get app version number
$version = System::versionNumber('Safari');
$version = System::versionNumber('Safari', 'CFBundleShortVersionString');

// Suppress error dialogs
$version = System::versionNumber('Safari', displayExceptions: false);

Methods:

Method Parameters Returns
applicationsFolder string
moveApp string $name, string $path, bool $launch = true void
versionNumber string $appName, string $key = 'CFBundleVersion', bool $displayExceptions = true ?string

Music

Interact with the macOS Music app and play system sounds.

use Pulli\Pullbox\Music;
use Pulli\Pullbox\Enums\SystemSound;

// Export a playlist
Music::exportPlaylist('My Playlist', '/path/to/export.xml');
Music::exportPlaylist('My Playlist', '/path/to/export.m3u', 'm3u');

// Play a system sound
Music::playSystemSound(SystemSound::Glass);
Music::playSystemSound(SystemSound::Ping);

Methods:

Method Parameters Returns
exportPlaylist string $name, string $to, string $format = 'xml' void
playSystemSound SystemSound $sound void

DEVONthink

Interact with DEVONthink via AppleScript. All 119 scripting commands are supported. Most methods are compatible with DEVONthink 3 and 4; AI/Chat methods require DEVONthink 4.

Records are referenced by UUID throughout. Methods that create or find records return their UUID.

use Pulli\Pullbox\DEVONthink;
use Pulli\Pullbox\Enums\RecordType;
use Pulli\Pullbox\Enums\UpdateMode;
use Pulli\Pullbox\Enums\SummaryType;

// Create a record
$uuid = DEVONthink::createRecordWith([
    'name' => 'My Note',
    'type' => RecordType::Markdown,
    'content' => '# Hello World',
]);

// Create in a specific group
$uuid = DEVONthink::createRecordWith(
    ['name' => 'Note', 'type' => RecordType::Text, 'content' => 'Hello'],
    $groupUuid
);

// Search
$uuids = DEVONthink::search('kind:markdown name:report');

// Import, index, export
$uuid = DEVONthink::importPath('/path/to/file.pdf', $groupUuid);
$uuid = DEVONthink::indexPath('/path/to/folder', $groupUuid);
DEVONthink::export($uuid, '/path/to/export');

// Web capture
$uuid = DEVONthink::createMarkdownFrom('https://example.com');
$uuid = DEVONthink::createPdfDocumentFrom('https://example.com');
$uuid = DEVONthink::downloadUrl('https://example.com/file.zip');

// Record operations
DEVONthink::move($uuid, $targetGroupUuid);
DEVONthink::duplicate($uuid);
DEVONthink::replicate($uuid, $targetGroupUuid);
DEVONthink::delete($uuid);

// Content access
$text = DEVONthink::getTextOf($uuid);
$path = DEVONthink::pathToRecord($uuid);
$contents = DEVONthink::getRecordContents($uuid);
DEVONthink::savePlainTextToRecord('Updated text', $uuid);
DEVONthink::update($uuid, 'Appended text', UpdateMode::Appending);

// Custom metadata
DEVONthink::addCustomMetaData('value', 'myKey', $uuid);
$value = DEVONthink::getCustomMetaData('myKey', $uuid);

// Database operations
$dbName = DEVONthink::createDatabase('/path/to/new.dtBase2');
$dbName = DEVONthink::openDatabase('/path/to/existing.dtBase2');
DEVONthink::optimize('My Database');
$errors = DEVONthink::verify('My Database');

// AI & Chat (DEVONthink 4 only)
$response = DEVONthink::getChatResponseForMessage('Summarize this', engine: 'openai');
$transcript = DEVONthink::transcribe($audioUuid, language: 'en');
$summaryUuid = DEVONthink::summarizeAnnotationsOf($uuids, SummaryType::Markdown);

Record CRUD

Method Parameters Returns
createRecordWith array $properties, ?string $groupUuid ?string
delete string $uuid bool
duplicate string $uuid, ?string $toGroupUuid ?string
move string $uuid, string $toGroupUuid ?string
moveIntoDatabase string $uuid, string $databaseName ?string
moveToExternalFolder string $uuid bool
replicate string $uuid, string $toGroupUuid ?string
merge array $uuids, ?string $groupUuid ?string
update string $uuid, string $text, UpdateMode $mode, ?string $url bool

Record Access

Method Parameters Returns
pathToRecord string $uuid string
getRecordContents string $uuid string
getRecordAt string $path, ?string $databaseName ?string
getRecordWithId int $id, ?string $databaseName ?string
getRecordWithUuid string $uuid ?string
getDatabaseWithId int $id ?string
getDatabaseWithUuid string $uuid ?string

Record Content

Method Parameters Returns
getTextOf string $uuid string
getRichTextOf string $uuid string
getTitleOf string $uuid string
getMetadataOf string $uuid string
getConcordanceOf string $uuid string
getLinksOf string $uuid array
getEmbeddedImagesOf string $uuid array
getEmbeddedObjectsOf string $uuid array
getEmbeddedSheetsAndScriptsOf string $uuid array
getFramesOf string $uuid array
getFaviconOf string $uuid string

Custom Meta Data

Method Parameters Returns
addCustomMetaData mixed $value, string $forKey, string $uuid void
getCustomMetaData string $forKey, string $uuid string

Search & Lookup

Method Parameters Returns
search string $query, ?string $inGroupUuid, ?string $comparison, bool $excludeSubgroups array
lookupRecordsWithComment string $comment, ?string $databaseName array
lookupRecordsWithContentHash string $hash, ?string $databaseName array
lookupRecordsWithFile string $path, ?string $databaseName array
lookupRecordsWithPath string $path, ?string $databaseName array
lookupRecordsWithTags array $tags, ?string $databaseName array
lookupRecordsWithUrl string $url, ?string $databaseName array
exists string $uuid bool
existsRecordAt string $path, ?string $databaseName bool
existsRecordWithComment string $comment, ?string $databaseName bool
existsRecordWithContentHash string $hash, ?string $databaseName bool
existsRecordWithFile string $path, ?string $databaseName bool
existsRecordWithPath string $path, ?string $databaseName bool
existsRecordWithUrl string $url, ?string $databaseName bool
classify string $uuid, ?string $groupUuid array
compare string $uuid, string $toUuid float
count string $uuid int

Import & Export

Method Parameters Returns
importRecords array|string $paths void
importPath string $path, ?string $groupUuid ?string
importTemplate string $path, ?string $groupUuid ?string
importAttachmentsOf string $uuid, ?string $groupUuid array
indexPath string $path, ?string $groupUuid ?string
export string $uuid, string $toPath bool
exportTagsOf string $uuid string
exportWebsite string $uuid, string $toPath bool

Web Capture & Download

Method Parameters Returns
createWebDocumentFrom string $url, ?string $groupUuid ?string
createMarkdownFrom string $url, ?string $groupUuid ?string
createPdfDocumentFrom string $url, ?string $groupUuid ?string
createFormattedNoteFrom string $url, ?string $groupUuid ?string
downloadUrl string $url, ?string $groupUuid ?string
downloadMarkupFrom string $url string
addDownload string $url void
addReadingList string $url void
getCachedDataForUrl string $url string
startDownloads bool
stopDownloads bool

Conversion

Method Parameters Returns
convert string $uuid, string $toType, ?string $groupUuid ?string
convertFeedToHtml string $uuid string
convertImage string $uuid, ?string $groupUuid ?string

Text & OCR

Method Parameters Returns
savePlainTextToRecord string $text, string $uuid void
extractKeywordsFrom string $text string
ocr string $uuid, ?string $groupUuid ?string

Database Operations

Method Parameters Returns
createDatabase string $path ?string
openDatabase string $path ?string
optimize string $databaseName bool
verify string $databaseName int
checkFileIntegrityOf string $uuid int
compress string $databaseName bool
synchronize ?string $uuid, ?string $databaseName bool
createLocation string $path, ?string $databaseName ?string

Thumbnails

Method Parameters Returns
createThumbnail string $uuid bool
deleteThumbnail string $uuid bool
updateThumbnail string $uuid bool

Versions

Method Parameters Returns
getVersionsOf string $uuid array
saveVersionOf string $uuid ?string
restoreRecordWith string $versionUuid bool

AI & Chat (DEVONthink 4 only)

Method Parameters Returns
getChatCapabilitiesForEngine string $engine string
getChatModelsForEngine string $engine string
getChatResponseForMessage string $message, ?string $engine, ?string $model string
downloadImageForPrompt string $prompt, ?string $engine string
summarizeText string $text, ?string $style string
summarizeAnnotationsOf array $uuids, SummaryType $type, ?string $groupUuid ?string
summarizeContentsOf array $uuids, SummaryType $type, ?string $style, ?string $groupUuid ?string
summarizeMentionsOf array $uuids, SummaryType $type, ?string $groupUuid ?string
transcribe string $uuid, ?string $language, ?bool $timestamps string

Feeds

Method Parameters Returns
getFeedItemsOf string $uuid array
getItemsOfFeed string $uuid array
refresh string $uuid bool

Sheets

Method Parameters Returns
addRow string $uuid void
deleteRowAt int $row, string $uuid void
getCellAt int $column, int $row, string $uuid string
setCellAt int $column, int $row, string $value, string $uuid bool

UI Dialogs

Method Parameters Returns
displayAuthenticationDialog string $message, ?string $title array
displayChatDialog ?string $message, ?string $title string
displayDateEditor ?string $date, ?string $title string
displayGroupSelector string $message, ?string $title, ?string $databaseName ?string
displayNameEditor string $message, ?string $title, ?string $defaultName string

UI Windows & Tabs

Method Parameters Returns
open string $uuid void
openTabFor ?string $uuid, ?string $url, ?string $referrer void
openWindowFor string $uuid, bool $force void
showSearch ?string $query void

Progress Indicator

Method Parameters Returns
showProgressIndicator string $title, ?bool $cancelButton, ?int $steps bool
stepProgressIndicator ?string $info bool
hideProgressIndicator bool

Workspaces

Method Parameters Returns
saveWorkspace string $name bool
loadWorkspace string $name void
deleteWorkspace string $name void

Smart Rules

Method Parameters Returns
performSmartRule ?string $name, ?string $uuid, ?string $trigger bool

Imprinting

Method Parameters Returns
imprint string $uuid, ?string $configuration void
imprintConfiguration string $name string
imprinterConfigurationNames array

Misc

Method Parameters Returns
addReminder string $uuid, ?string $alarm void
logMessage string $message, ?string $info void
pasteClipboard ?string $groupUuid ?string
make string $type, array $properties, ?string $groupUuid ?string

AppleScript

Low-level class for generating and executing AppleScript code. Use this to generate scripts without executing them, or to execute arbitrary scripts safely.

use Pulli\Pullbox\AppleScript;

// Execute a script (fire and forget)
AppleScript::execute($script);

// Execute and capture output
$result = AppleScript::executeAndCapture($script);

// Escape user input for safe AppleScript string interpolation
$safe = AppleScript::escapeString('He said "hello"');

// Generate script strings
$script = AppleScript::displayDialog('Hello', 'Title');
$script = AppleScript::displayNotification('Done', 'App', 'Subtitle', 'Glass');
$script = AppleScript::applicationsFolder();
$script = AppleScript::moveApp('MyApp', '/path/to/app');
$script = AppleScript::musicExportPlaylist('Playlist', '/path/to/file', 'xml');
$script = AppleScript::devonthinkPathToRecord('uuid');
$scripts = AppleScript::devonthinkImportRecords(['/path/to/file']);
$script = AppleScript::devonthinkSavePlainTextToRecord('text', 'uuid');

Enums

RecordType

use Pulli\Pullbox\Enums\RecordType;

RecordType::Bookmark;       // 'bookmark'
RecordType::Feed;           // 'feed'
RecordType::FormattedNote;  // 'formatted note'
RecordType::Group;          // 'group'
RecordType::HTML;           // 'html'
RecordType::Markdown;       // 'markdown'
RecordType::Picture;        // 'picture'
RecordType::Plist;          // 'plist'
RecordType::Quicktime;      // 'quicktime'
RecordType::RTF;            // 'rtf'
RecordType::RTFD;           // 'rtfd'
RecordType::Script;         // 'script'
RecordType::Sheet;          // 'sheet'
RecordType::SmartGroup;     // 'smart group'
RecordType::Text;           // 'txt'
RecordType::WebArchive;     // 'web archive'
RecordType::Unknown;        // 'unknown'

UpdateMode

use Pulli\Pullbox\Enums\UpdateMode;

UpdateMode::Appending;   // 'appending'
UpdateMode::Inserting;   // 'inserting'
UpdateMode::Replacing;   // 'replacing'

SummaryType

use Pulli\Pullbox\Enums\SummaryType;

SummaryType::Markdown;  // 'markdown'
SummaryType::Rich;      // 'rich'
SummaryType::Sheet;     // 'sheet'
SummaryType::Simple;    // 'simple'

PlaylistExportFormat

use Pulli\Pullbox\Enums\PlaylistExportFormat;

// Available formats
PlaylistExportFormat::M3U;         // 'M3U'
PlaylistExportFormat::M3U8;        // 'M3U8'
PlaylistExportFormat::PlainText;   // 'plain text'
PlaylistExportFormat::UnicodeText; // 'Unicode text'
PlaylistExportFormat::XML;         // 'XML'

// Resolve from string
$format = PlaylistExportFormat::fromInput('m3u');  // PlaylistExportFormat::M3U

SystemSound

use Pulli\Pullbox\Enums\SystemSound;

// Available sounds
SystemSound::Basso;
SystemSound::Blow;
SystemSound::Bottle;
SystemSound::Frog;
SystemSound::Funk;
SystemSound::Glass;
SystemSound::Hero;
SystemSound::Morse;
SystemSound::Ping;
SystemSound::Pop;
SystemSound::Purr;
SystemSound::Sosumi;
SystemSound::Submarine;
SystemSound::Tink;

// Get the file path of a sound
$path = SystemSound::Glass->filepath(); // /System/Library/Sounds/Glass.aiff

Testing

# Run all tests (excluding macOS-only)
composer test -- --exclude-group=mac

# Run all tests (on macOS)
composer test

# Run a specific test file
vendor/bin/pest tests/Unit/AppleScriptTest.php

Credits

License

The MIT License (MIT). Please see License File for more information.