|
724
|
17
|
9
|
2026-04-11T12:09:57.390178+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775909397390_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
)
/* frames_fts(full_text,app_name,window_name,bro )
/* frames_fts(full_text,app_name,window_name,browser_url,id) */;
CREATE TABLE IF NOT EXISTS 'frames_fts_data'(id INTEGER PRIMARY KEY, block BLOB);
CREATE TABLE IF NOT EXISTS 'frames_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;
CREATE TABLE IF NOT EXISTS 'frames_fts_content'(id INTEGER PRIMARY KEY, c0, c1, c2, c3, c4);
CREATE TABLE IF NOT EXISTS 'frames_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB);
CREATE TABLE IF NOT EXISTS 'frames_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID;
CREATE TRIGGER frames_ai AFTER INSERT ON frames
WHEN NEW.full_text IS NOT NULL AND NEW.full_text != ''
BEGIN
INSERT INTO frames_fts(id, full_text, app_name, window_name, browser_url)
VALUES (
NEW.id,
NEW.full_text,
COALESCE(NEW.app_name, ''),
COALESCE(NEW.window_name, ''),
COALESCE(NEW.browser_url, '')
);
END;
CREATE TRIGGER frames_au AFTER UPDATE OF full_text, app_name, window_name, browser_url ON frames
BEGIN
DELETE FROM frames_fts WHERE id = OLD.id;
INSERT INTO frames_fts(id, full_text, app_name, window_name, browser_url)
SELECT
NEW.id,
COALESCE(NEW.full_text, ''),
COALESCE(NEW.app_name, ''),
COALESCE(NEW.window_name, ''),
COALESCE(NEW.browser_url, '')
WHERE NEW.full_text IS NOT NULL AND NEW.full_text != '';
END;
CREATE TRIGGER frames_ad AFTER DELETE ON frames
BEGIN
DELETE FROM frames_fts WHERE id = OLD.id;
END;
CREATE INDEX idx_memories_frame_id ON memories(frame_id);
CREATE INDEX idx_elements_source_role_text
ON elements(source, role, frame_id)
WHERE text IS NOT NULL;
CREATE INDEX idx_frames_sync_id ON frames(sync_id) WHERE sync_id IS NOT NULL;
CREATE INDEX idx_ocr_text_sync_id ON ocr_text(sync_id) WHERE sync_id IS NOT NULL;
CREATE INDEX idx_audio_transcriptions_sync_id ON audio_transcriptions(sync_id) WHERE sync_id IS NOT NULL;
CREATE INDEX idx_elements_frame_source_role ON elements(frame_id, source, role) WHERE text IS NOT NULL;
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT date(timestamp), COUNT(*) as frames FROM frames GROUP BY date(timestamp) ORDER BY date(timestamp);"
2026-04-09|523
2026-04-11|197
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":")\n/* frames_fts(full_text,app_name,window_name,browser_url,id) */;\nCREATE TABLE IF NOT EXISTS 'frames_fts_data'(id INTEGER PRIMARY KEY, block BLOB);\nCREATE TABLE IF NOT EXISTS 'frames_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;\nCREATE TABLE IF NOT EXISTS 'frames_fts_content'(id INTEGER PRIMARY KEY, c0, c1, c2, c3, c4);\nCREATE TABLE IF NOT EXISTS 'frames_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB);\nCREATE TABLE IF NOT EXISTS 'frames_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID;\nCREATE TRIGGER frames_ai AFTER INSERT ON frames\nWHEN NEW.full_text IS NOT NULL AND NEW.full_text != ''\nBEGIN\n INSERT INTO frames_fts(id, full_text, app_name, window_name, browser_url)\n VALUES (\n NEW.id,\n NEW.full_text,\n COALESCE(NEW.app_name, ''),\n COALESCE(NEW.window_name, ''),\n COALESCE(NEW.browser_url, '')\n );\nEND;\nCREATE TRIGGER frames_au AFTER UPDATE OF full_text, app_name, window_name, browser_url ON frames\nBEGIN\n DELETE FROM frames_fts WHERE id = OLD.id;\n INSERT INTO frames_fts(id, full_text, app_name, window_name, browser_url)\n SELECT\n NEW.id,\n COALESCE(NEW.full_text, ''),\n COALESCE(NEW.app_name, ''),\n COALESCE(NEW.window_name, ''),\n COALESCE(NEW.browser_url, '')\n WHERE NEW.full_text IS NOT NULL AND NEW.full_text != '';\nEND;\nCREATE TRIGGER frames_ad AFTER DELETE ON frames\nBEGIN\n DELETE FROM frames_fts WHERE id = OLD.id;\nEND;\nCREATE INDEX idx_memories_frame_id ON memories(frame_id);\nCREATE INDEX idx_elements_source_role_text\n ON elements(source, role, frame_id)\n WHERE text IS NOT NULL;\nCREATE INDEX idx_frames_sync_id ON frames(sync_id) WHERE sync_id IS NOT NULL;\nCREATE INDEX idx_ocr_text_sync_id ON ocr_text(sync_id) WHERE sync_id IS NOT NULL;\nCREATE INDEX idx_audio_transcriptions_sync_id ON audio_transcriptions(sync_id) WHERE sync_id IS NOT NULL;\nCREATE INDEX idx_elements_frame_source_role ON elements(frame_id, source, role) WHERE text IS NOT NULL;\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT date(timestamp), COUNT(*) as frames FROM frames GROUP BY date(timestamp) ORDER BY date(timestamp);\"\n2026-04-09|523\n2026-04-11|197\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":")\n/* frames_fts(full_text,app_name,window_name,browser_url,id) */;\nCREATE TABLE IF NOT EXISTS 'frames_fts_data'(id INTEGER PRIMARY KEY, block BLOB);\nCREATE TABLE IF NOT EXISTS 'frames_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;\nCREATE TABLE IF NOT EXISTS 'frames_fts_content'(id INTEGER PRIMARY KEY, c0, c1, c2, c3, c4);\nCREATE TABLE IF NOT EXISTS 'frames_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB);\nCREATE TABLE IF NOT EXISTS 'frames_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID;\nCREATE TRIGGER frames_ai AFTER INSERT ON frames\nWHEN NEW.full_text IS NOT NULL AND NEW.full_text != ''\nBEGIN\n INSERT INTO frames_fts(id, full_text, app_name, window_name, browser_url)\n VALUES (\n NEW.id,\n NEW.full_text,\n COALESCE(NEW.app_name, ''),\n COALESCE(NEW.window_name, ''),\n COALESCE(NEW.browser_url, '')\n );\nEND;\nCREATE TRIGGER frames_au AFTER UPDATE OF full_text, app_name, window_name, browser_url ON frames\nBEGIN\n DELETE FROM frames_fts WHERE id = OLD.id;\n INSERT INTO frames_fts(id, full_text, app_name, window_name, browser_url)\n SELECT\n NEW.id,\n COALESCE(NEW.full_text, ''),\n COALESCE(NEW.app_name, ''),\n COALESCE(NEW.window_name, ''),\n COALESCE(NEW.browser_url, '')\n WHERE NEW.full_text IS NOT NULL AND NEW.full_text != '';\nEND;\nCREATE TRIGGER frames_ad AFTER DELETE ON frames\nBEGIN\n DELETE FROM frames_fts WHERE id = OLD.id;\nEND;\nCREATE INDEX idx_memories_frame_id ON memories(frame_id);\nCREATE INDEX idx_elements_source_role_text\n ON elements(source, role, frame_id)\n WHERE text IS NOT NULL;\nCREATE INDEX idx_frames_sync_id ON frames(sync_id) WHERE sync_id IS NOT NULL;\nCREATE INDEX idx_ocr_text_sync_id ON ocr_text(sync_id) WHERE sync_id IS NOT NULL;\nCREATE INDEX idx_audio_transcriptions_sync_id ON audio_transcriptions(sync_id) WHERE sync_id IS NOT NULL;\nCREATE INDEX idx_elements_frame_source_role ON elements(frame_id, source, role) WHERE text IS NOT NULL;\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT date(timestamp), COUNT(*) as frames FROM frames GROUP BY date(timestamp) ORDER BY date(timestamp);\"\n2026-04-09|523\n2026-04-11|197\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.140625,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.140625,"top":0.05888889,"width":0.140625,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.14479166,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.28125,"top":0.05888889,"width":0.140625,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.28541666,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.421875,"top":0.05888889,"width":0.140625,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.42604166,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5625,"top":0.05888889,"width":0.14027777,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.56666666,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.7027778,"top":0.05888889,"width":0.14027777,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.70694447,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.84305555,"top":0.05888889,"width":0.14027777,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.8472222,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.48819444,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
-332181317459790359
|
7936206669614402548
|
click
|
accessibility
|
NULL
|
)
/* frames_fts(full_text,app_name,window_name,bro )
/* frames_fts(full_text,app_name,window_name,browser_url,id) */;
CREATE TABLE IF NOT EXISTS 'frames_fts_data'(id INTEGER PRIMARY KEY, block BLOB);
CREATE TABLE IF NOT EXISTS 'frames_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;
CREATE TABLE IF NOT EXISTS 'frames_fts_content'(id INTEGER PRIMARY KEY, c0, c1, c2, c3, c4);
CREATE TABLE IF NOT EXISTS 'frames_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB);
CREATE TABLE IF NOT EXISTS 'frames_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID;
CREATE TRIGGER frames_ai AFTER INSERT ON frames
WHEN NEW.full_text IS NOT NULL AND NEW.full_text != ''
BEGIN
INSERT INTO frames_fts(id, full_text, app_name, window_name, browser_url)
VALUES (
NEW.id,
NEW.full_text,
COALESCE(NEW.app_name, ''),
COALESCE(NEW.window_name, ''),
COALESCE(NEW.browser_url, '')
);
END;
CREATE TRIGGER frames_au AFTER UPDATE OF full_text, app_name, window_name, browser_url ON frames
BEGIN
DELETE FROM frames_fts WHERE id = OLD.id;
INSERT INTO frames_fts(id, full_text, app_name, window_name, browser_url)
SELECT
NEW.id,
COALESCE(NEW.full_text, ''),
COALESCE(NEW.app_name, ''),
COALESCE(NEW.window_name, ''),
COALESCE(NEW.browser_url, '')
WHERE NEW.full_text IS NOT NULL AND NEW.full_text != '';
END;
CREATE TRIGGER frames_ad AFTER DELETE ON frames
BEGIN
DELETE FROM frames_fts WHERE id = OLD.id;
END;
CREATE INDEX idx_memories_frame_id ON memories(frame_id);
CREATE INDEX idx_elements_source_role_text
ON elements(source, role, frame_id)
WHERE text IS NOT NULL;
CREATE INDEX idx_frames_sync_id ON frames(sync_id) WHERE sync_id IS NOT NULL;
CREATE INDEX idx_ocr_text_sync_id ON ocr_text(sync_id) WHERE sync_id IS NOT NULL;
CREATE INDEX idx_audio_transcriptions_sync_id ON audio_transcriptions(sync_id) WHERE sync_id IS NOT NULL;
CREATE INDEX idx_elements_frame_source_role ON elements(frame_id, source, role) WHERE text IS NOT NULL;
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT date(timestamp), COUNT(*) as frames FROM frames GROUP BY date(timestamp) ORDER BY date(timestamp);"
2026-04-09|523
2026-04-11|197
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
723
|
|
53739
|
1163
|
10
|
2026-04-20T08:26:42.245866+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776673602245_m2.jpg...
|
PhpStorm
|
faVsco.js – AppServiceProvider.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
.cursor, folder
.github...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.034242023,"height":0.025538707},"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.49168882,"top":0.17478053,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.50099736,"top":0.17478053,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.5106383,"top":0.17318435,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.51795214,"top":0.17318435,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","depth":4,"bounds":{"left":0.13863032,"top":0.17158818,"width":0.3863032,"height":0.8076616},"value":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.9527925,"top":0.10055866,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"bounds":{"left":0.96276593,"top":0.10055866,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","depth":4,"value":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Install","depth":3,"bounds":{"left":0.90957445,"top":0.07821229,"width":0.013297873,"height":0.013567438},"help_text":"Installs packages from composer.json, taking account of composer.lock","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Update","depth":3,"bounds":{"left":0.9281915,"top":0.07821229,"width":0.016289894,"height":0.013567438},"help_text":"Installs latest appropriate versions of packages from composer.json","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Show log","depth":3,"bounds":{"left":0.94980055,"top":0.07821229,"width":0.020279255,"height":0.013567438},"help_text":"Show log of Composer-related actions","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"}]...
|
2381543966077037924
|
7935554553542549854
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
.cursor, folder
.github...
|
53737
|
|
54156
|
1166
|
94
|
2026-04-20T08:40:21.902728+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674421902_m1.jpg...
|
PhpStorm
|
faVsco.js – AppServiceProvider.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","depth":4,"value":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Install","depth":3,"help_text":"Installs packages from composer.json, taking account of composer.lock","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Update","depth":3,"help_text":"Installs latest appropriate versions of packages from composer.json","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Show log","depth":3,"help_text":"Show log of Composer-related actions","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3155554097147706318
|
7935554484823069534
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
54154
|
|
54157
|
1168
|
79
|
2026-04-20T08:40:21.807524+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674421807_m2.jpg...
|
PhpStorm
|
faVsco.js – AppServiceProvider.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.034242023,"height":0.025538707},"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.48238033,"top":0.19952115,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.49168882,"top":0.19952115,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.50166225,"top":0.19952115,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.5106383,"top":0.19792499,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.51795214,"top":0.19792499,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","depth":4,"bounds":{"left":0.13863032,"top":0.1963288,"width":0.3863032,"height":0.782921},"value":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.9527925,"top":0.10055866,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"bounds":{"left":0.96276593,"top":0.10055866,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","depth":4,"value":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Install","depth":3,"bounds":{"left":0.90957445,"top":0.07821229,"width":0.013297873,"height":0.013567438},"help_text":"Installs packages from composer.json, taking account of composer.lock","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Update","depth":3,"bounds":{"left":0.9281915,"top":0.07821229,"width":0.016289894,"height":0.013567438},"help_text":"Installs latest appropriate versions of packages from composer.json","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Show log","depth":3,"bounds":{"left":0.94980055,"top":0.07821229,"width":0.020279255,"height":0.013567438},"help_text":"Show log of Composer-related actions","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3155554097147706318
|
7935554484823069534
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
54180
|
1169
|
4
|
2026-04-20T08:42:41.879859+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674561879_m1.jpg...
|
PhpStorm
|
faVsco.js – AppServiceProvider.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","depth":4,"value":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Install","depth":3,"help_text":"Installs packages from composer.json, taking account of composer.lock","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Update","depth":3,"help_text":"Installs latest appropriate versions of packages from composer.json","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Show log","depth":3,"help_text":"Show log of Composer-related actions","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3155554097147706318
|
7935554484823069534
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
54178
|
|
54181
|
1170
|
3
|
2026-04-20T08:42:42.945427+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674562945_m2.jpg...
|
PhpStorm
|
faVsco.js – AppServiceProvider.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.034242023,"height":0.025538707},"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.48238033,"top":0.19952115,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.49168882,"top":0.19952115,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.50166225,"top":0.19952115,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.5106383,"top":0.19792499,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.51795214,"top":0.19792499,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","depth":4,"bounds":{"left":0.13863032,"top":0.1963288,"width":0.3863032,"height":0.782921},"value":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.9527925,"top":0.10055866,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"bounds":{"left":0.96276593,"top":0.10055866,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","depth":4,"value":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Install","depth":3,"bounds":{"left":0.90957445,"top":0.07821229,"width":0.013297873,"height":0.013567438},"help_text":"Installs packages from composer.json, taking account of composer.lock","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Update","depth":3,"bounds":{"left":0.9281915,"top":0.07821229,"width":0.016289894,"height":0.013567438},"help_text":"Installs latest appropriate versions of packages from composer.json","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Show log","depth":3,"bounds":{"left":0.94980055,"top":0.07821229,"width":0.020279255,"height":0.013567438},"help_text":"Show log of Composer-related actions","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3155554097147706318
|
7935554484823069534
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
54183
|
1170
|
5
|
2026-04-20T08:43:06.034106+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674586034_m2.jpg...
|
PhpStorm
|
faVsco.js – AppServiceProvider.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.034242023,"height":0.025538707},"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.48238033,"top":0.19952115,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.49168882,"top":0.19952115,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.50166225,"top":0.19952115,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.5106383,"top":0.19792499,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.51795214,"top":0.19792499,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","depth":4,"bounds":{"left":0.13863032,"top":0.1963288,"width":0.3863032,"height":0.782921},"value":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.9527925,"top":0.10055866,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"bounds":{"left":0.96276593,"top":0.10055866,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","depth":4,"value":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Install","depth":3,"bounds":{"left":0.90957445,"top":0.07821229,"width":0.013297873,"height":0.013567438},"help_text":"Installs packages from composer.json, taking account of composer.lock","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Update","depth":3,"bounds":{"left":0.9281915,"top":0.07821229,"width":0.016289894,"height":0.013567438},"help_text":"Installs latest appropriate versions of packages from composer.json","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Show log","depth":3,"bounds":{"left":0.94980055,"top":0.07821229,"width":0.020279255,"height":0.013567438},"help_text":"Show log of Composer-related actions","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3155554097147706318
|
7935554484823069534
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
54186
|
1169
|
6
|
2026-04-20T08:43:09.716233+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674589716_m1.jpg...
|
PhpStorm
|
faVsco.js – AppServiceProvider.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","depth":4,"value":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Install","depth":3,"help_text":"Installs packages from composer.json, taking account of composer.lock","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Update","depth":3,"help_text":"Installs latest appropriate versions of packages from composer.json","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Show log","depth":3,"help_text":"Show log of Composer-related actions","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3155554097147706318
|
7935554484823069534
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
54185
|
|
54187
|
1170
|
7
|
2026-04-20T08:43:09.716262+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674589716_m2.jpg...
|
PhpStorm
|
faVsco.js – AppServiceProvider.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.034242023,"height":0.025538707},"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.48238033,"top":0.19952115,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.49168882,"top":0.19952115,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.50166225,"top":0.19952115,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.5106383,"top":0.19792499,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.51795214,"top":0.19792499,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","depth":4,"bounds":{"left":0.13863032,"top":0.1963288,"width":0.3863032,"height":0.782921},"value":"<?php\n\nnamespace Jiminny\\Providers;\n\nuse GuzzleHttp\\Client;\nuse Illuminate\\Routing\\Events\\RouteMatched;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Schema;\nuse Illuminate\\Support\\ServiceProvider;\nuse Jiminny\\Component\\BillingManagement\\Denormalizer;\nuse Jiminny\\Component\\BillingManagement\\MaxioClient;\nuse Jiminny\\Component\\BillingManagement\\Repositories\\ComponentMappingRepository;\nuse Jiminny\\Integrations\\Releases;\nuse Jiminny\\Models\\Account;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\Profile;\nuse Jiminny\\Models\\Lead;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Observers\\AccountObserver;\nuse Jiminny\\Observers\\ActivityObserver;\nuse Jiminny\\Observers\\ContactObserver;\nuse Jiminny\\Observers\\LeadObserver;\nuse Jiminny\\Observers\\ProfileObserver;\nuse Jiminny\\Observers\\SocialAccountObserver;\nuse Jiminny\\Services\\Internal\\WebhookTokenValidator;\nuse Jiminny\\Services\\Internal\\WebhookTokenGenerator;\nuse Jiminny\\Services\\Internal\\WebhookForwarder;\nuse Laravel\\Passport\\Passport;\n\nfinal class AppServiceProvider extends ServiceProvider\n{\n /**\n * All the abilities that may be assigned to API tokens.\n */\n private const array TOKENS_CAN = [\n 'dial-outbound' => 'Dial with the Softphone',\n 'start-conference' => 'Organize a Conference',\n 'sms' => 'Send & receive SMS with the Softphone',\n 'ghost-conference' => 'Join a Conference as a Ghost',\n ];\n\n public function boot(): void\n {\n $this->app->bind(\n MaxioClient::class,\n fn () => new MaxioClient(\n httpClient: new Client([\n 'base_uri' => config('maxio.api-route'),\n 'headers' => [\n 'Accept' => 'application/json',\n 'Content-Type' => 'application/json',\n ],\n 'auth' => [config('maxio.api-key'), config('maxio.password')],\n ]),\n denormalizer: $this->app->get(Denormalizer::class),\n componentMappingRepository: $this->app->get(ComponentMappingRepository::class),\n ),\n );\n\n $this->app->bind(\n WebhookTokenValidator::class,\n fn () => new WebhookTokenValidator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookTokenGenerator::class,\n fn () => new WebhookTokenGenerator(\n secret: config('services.internal_webhook.secret'),\n ),\n );\n\n $this->app->bind(\n WebhookForwarder::class,\n fn () => new WebhookForwarder(\n tokenGenerator: $this->app->get(WebhookTokenGenerator::class),\n httpClient: new Client(),\n ),\n );\n\n $this->app['router']->matched(function (RouteMatched $e) {\n $route = $e->route;\n\n if (! Arr::has($route->getAction(), 'guard')) {\n return;\n }\n\n $routeGuard = Arr::get($route->getAction(), 'guard');\n\n $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {\n return $this->app['auth']->guard($routeGuard)->user();\n });\n\n $this->app['auth']->setDefaultDriver($routeGuard);\n });\n\n // Inject the controller name into the Body tag.\n $this->app['view']->composer('layouts.header', function ($view) {\n if (app('request')->route()) {\n $action = app('request')->route()->getAction();\n\n $controller = class_basename($action['controller']);\n\n [$controller, $action] = explode('@', str_replace('Controller', '', $controller));\n\n $view->with(compact('controller', 'action'));\n } else {\n $view->with(['controller' => null]);\n }\n });\n\n $this->app->singleton(\\Jiminny\\Services\\ActivityService::class);\n\n Blade::directive('feature', function ($expression) {\n return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';\n });\n\n Blade::directive('endfeature', function ($expression) {\n return '<?php endif; ?>';\n });\n\n Schema::defaultStringLength(191);\n\n Passport::$clientUuids = false;\n\n Passport::$registersJsonApiRoutes = true;\n\n Passport::tokensCan(self::TOKENS_CAN);\n\n Passport::withoutCookieSerialization();\n\n SocialAccount::observe(SocialAccountObserver::class);\n Profile::observe(ProfileObserver::class);\n Activity::observe(ActivityObserver::class);\n Lead::observe(LeadObserver::class);\n Contact::observe(ContactObserver::class);\n Account::observe(AccountObserver::class);\n }\n\n /**\n * Register any application services.\n */\n public function register(): void\n {\n if ($this->app->environment() === 'local') {\n $this->app->register(\\Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider::class);\n }\n\n $this->registerEncryptCookies();\n\n $this->registerReleases();\n }\n\n /**\n * Register the releases class.\n */\n protected function registerReleases()\n {\n $this->app->singleton(Releases::class, function ($app) {\n $cache = $app['cache.store'];\n $token = $app['config']->get('services.github.token');\n\n return new Releases($cache, $token);\n });\n }\n\n /**\n * Register encrypt cookies.\n */\n protected function registerEncryptCookies(): void\n {\n $this->app->singleton(\\Jiminny\\Http\\Middleware\\EncryptCookies::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.9527925,"top":0.10055866,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"bounds":{"left":0.96276593,"top":0.10055866,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","depth":4,"value":"{\n \"name\": \"jiminny/app\",\n \"description\": \"The Jiminny Platform.\",\n \"keywords\": [\n \"training\",\n \"salesforce\",\n \"conference\"\n ],\n \"license\": \"MIT\",\n \"type\": \"project\",\n \"require\": {\n \"php\": \"^8.3\",\n \"ext-ctype\": \"*\",\n \"ext-curl\": \"*\",\n \"ext-date\": \"*\",\n \"ext-dom\": \"*\",\n \"ext-fileinfo\": \"*\",\n \"ext-filter\": \"*\",\n \"ext-gd\": \"*\",\n \"ext-gmp\": \"*\",\n \"ext-hash\": \"*\",\n \"ext-iconv\": \"*\",\n \"ext-igbinary\": \"*\",\n \"ext-imagick\": \"*\",\n \"ext-intl\": \"*\",\n \"ext-json\": \"*\",\n \"ext-libxml\": \"*\",\n \"ext-mailparse\": \"*\",\n \"ext-mbstring\": \"*\",\n \"ext-mysqlnd\": \"*\",\n \"ext-openssl\": \"*\",\n \"ext-pcntl\": \"*\",\n \"ext-pcre\": \"*\",\n \"ext-pdo\": \"*\",\n \"ext-pdo_mysql\": \"*\",\n \"ext-phar\": \"*\",\n \"ext-phpiredis\": \"*\",\n \"ext-posix\": \"*\",\n \"ext-readline\": \"*\",\n \"ext-redis\": \"*\",\n \"ext-reflection\": \"*\",\n \"ext-session\": \"*\",\n \"ext-simplexml\": \"*\",\n \"ext-sockets\": \"*\",\n \"ext-spl\": \"*\",\n \"ext-tokenizer\": \"*\",\n \"ext-xml\": \"*\",\n \"ext-xmlreader\": \"*\",\n \"ext-xmlwriter\": \"*\",\n \"ext-zend-opcache\": \"*\",\n \"ext-zip\": \"*\",\n \"ext-zlib\": \"*\",\n \"lib-curl\": \"*\",\n \"lib-curl-openssl\": \"*\",\n \"lib-curl-zlib\": \"*\",\n \"lib-date-timelib\": \"*\",\n \"lib-date-zoneinfo\": \"*\",\n \"lib-fileinfo-libmagic\": \"*\",\n \"lib-gd\": \"*\",\n \"lib-gd-freetype\": \"*\",\n \"lib-gd-libjpeg\": \"*\",\n \"lib-gd-libpng\": \"*\",\n \"lib-gmp\": \"*\",\n \"lib-icu\": \"*\",\n \"lib-icu-cldr\": \"*\",\n \"lib-icu-unicode\": \"*\",\n \"lib-imagick-imagemagick\": \"*\",\n \"lib-libxml\": \"*\",\n \"lib-mbstring-libmbfl\": \"*\",\n \"lib-mbstring-oniguruma\": \"*\",\n \"lib-openssl\": \"*\",\n \"lib-pcre\": \"*\",\n \"lib-pcre-unicode\": \"*\",\n \"lib-zip-libzip\": \"*\",\n \"lib-zlib\": \"*\",\n \"24slides/laravel-saml2\": \"^2.4\",\n \"adam-paterson/oauth2-slack\": \"^1.1\",\n \"asimlqt/php-google-spreadsheet-client\": \"^3.0\",\n \"aws/aws-sdk-php\": \"^3.368\",\n \"aws/aws-sdk-php-laravel\": \"^3.10\",\n \"bepsvpt/secure-headers\": \"^9.0\",\n \"chadhutchins/oauth2-slack\": \"^1.2\",\n \"chaseconey/laravel-datadog-helper\": \"^1.2\",\n \"chrisyue/php-m3u8\": \"4.0.3\",\n \"daniti/oauth2-pipedrive\": \"dev-master\",\n \"devio/pipedrive\": \"^2.6\",\n \"doctrine/dbal\": \"^4.0\",\n \"elasticsearch/elasticsearch\": \"^7.11\",\n \"erusev/parsedown\": \"^1.7\",\n \"fakerphp/faker\": \"^1.23\",\n \"firebase/php-jwt\": \"^7.0\",\n \"flipboxdigital/oauth2-hubspot\": \"1.0.1\",\n \"giggsey/libphonenumber-for-php\": \"^8.12\",\n \"google/apiclient\": \"^2.19\",\n \"google/apiclient-services\": \"~0.360\",\n \"google/apps-meet\": \"^0.5.1\",\n \"guzzlehttp/guzzle\": \"^7.8\",\n \"guzzlehttp/psr7\": \"^2.6\",\n \"halaxa/json-machine\": \"^1.2\",\n \"html2text/html2text\": \"^4.3\",\n \"hubspot/api-client\": \"~5.0.0\",\n \"hubspot/hubspot-php\": \"^5.2.0\",\n \"intercom/intercom-php\": \"^4.5\",\n \"intervention/image\": \"^3.4\",\n \"jakeasmith/http_build_url\": \"^1.0\",\n \"jdavidbakr/cloudfront-proxies\": \"^1.7\",\n \"jeremykendall/php-domain-parser\": \"^6.3\",\n \"jiminny/oauth2-aircall\": \"dev-master\",\n \"jiminny/oauth2-bullhorn\": \"^0.2.0\",\n \"jiminny/oauth2-dialpad\": \"dev-master\",\n \"jiminny/oauth2-salesloft\": \"dev-master\",\n \"jolicode/slack-php-api\": \"^4.5.0\",\n \"kalnoy/nestedset\": \"*\",\n \"laravel/framework\": \"^12.28\",\n \"laravel/helpers\": \"^1.7\",\n \"laravel/passport\": \"^13.0\",\n \"laravel/slack-notification-channel\": \"^3.4\",\n \"laravel/tinker\": \"^2.10.1\",\n \"laravel/ui\": \"^4.6\",\n \"laravolt/avatar\": \"^6.1\",\n \"league/flysystem\": \"^3.0\",\n \"league/flysystem-aws-s3-v3\": \"^3.0\",\n \"league/fractal\": \"*\",\n \"league/oauth2-client\": \"^2.7\",\n \"league/oauth2-google\": \"^4.0\",\n \"league/oauth2-linkedin\": \"^5.1\",\n \"league/oauth2-server\": \"^9.2\",\n \"league/statsd\": \"^2.0\",\n \"markrogoyski/math-php\": \"^2.7.0\",\n \"microsoft/microsoft-graph\": \"^2.51\",\n \"monolog/monolog\": \"^3.0\",\n \"nesbot/carbon\": \"^3.8\",\n \"nette/caching\": \"*\",\n \"phlib/sms-length\": \"^2.0\",\n \"php-ffmpeg/php-ffmpeg\": \"^1.2\",\n \"php-http/client-common\": \"^2.7\",\n \"php-http/curl-client\": \"^2.3\",\n \"php-http/httplug\": \"^2.2\",\n \"php-http/message\": \"^1.16\",\n \"phpseclib/phpseclib\": \"^3.0.36\",\n \"propaganistas/laravel-phone\": \"^5.3\",\n \"psr/cache\": \"^3.0\",\n \"psr/http-message\": \"^2.0\",\n \"psr/log\": \"^3.0\",\n \"psr/simple-cache\": \"^3.0\",\n \"pusher/pusher-php-server\": \"7.2.3\",\n \"ramsey/uuid\": \"^4.2\",\n \"ringcentral/ringcentral-php\": \"3.0.0\",\n \"rmccue/requests\": \"^2.0\",\n \"ruflin/elastica\": \"^7.1.1\",\n \"santigarcor/laratrust\": \"^8.4\",\n \"sentry/sentry\": \"4.13.0\",\n \"sentry/sentry-laravel\": \"~4.13.0\",\n \"shiftonelabs/laravel-sqs-fifo-queue\": \"^3.0\",\n \"spatie/fractalistic\": \"^2.9\",\n \"spatie/laravel-fractal\": \"^6.3\",\n \"spatie/laravel-ignition\": \"^2.9\",\n \"spatie/laravel-webhook-server\": \"^3.8\",\n \"staudenmeir/belongs-to-through\": \"^2.17\",\n \"stevenmaguire/oauth2-salesforce\": \"^2.0\",\n \"symfony/cache\": \"^7.2\",\n \"symfony/console\": \"^7.2\",\n \"symfony/css-selector\": \"^7.2\",\n \"symfony/debug\": \"^4.4\",\n \"symfony/dom-crawler\": \"^7.2\",\n \"symfony/expression-language\": \"^7.2\",\n \"symfony/finder\": \"^7.2\",\n \"symfony/http-client\": \"^7.3\",\n \"symfony/http-foundation\": \"^7.2\",\n \"symfony/http-kernel\": \"^7.2\",\n \"symfony/postmark-mailer\": \"^7.3\",\n \"symfony/process\": \"^7.3\",\n \"symfony/property-access\": \"^7.2\",\n \"symfony/psr-http-message-bridge\": \"^7.0\",\n \"symfony/var-dumper\": \"^7.2\",\n \"symfony/workflow\": \"^7.2\",\n \"tecnickcom/tcpdf\": \"^6.11\",\n \"thenetworg/oauth2-azure\": \"dev-master\",\n \"tmannherz/oauth2-ringcentral\": \"dev-master\",\n \"twilio/sdk\": \"^8.3\",\n \"vanderlee/php-sentence\": \"^1.0\",\n \"vinkla/hashids\": \"^13.0\",\n \"vlucas/phpdotenv\": \"^5.4\",\n \"wildbit/postmark-php\": \"^6.0\",\n \"willdurand/email-reply-parser\": \"^2.8\",\n \"zbateson/mail-mime-parser\": \"^3.0.4\"\n },\n \"require-dev\": {\n \"barryvdh/laravel-debugbar\": \"^3.15\",\n \"barryvdh/laravel-ide-helper\": \"^3.5\",\n \"brianium/paratest\": \"^7.5\",\n \"browserstack/browserstack-local\": \"^1.1.0\",\n \"filp/whoops\": \"^2.9\",\n \"friendsofphp/php-cs-fixer\": \"^3.66\",\n \"infection/infection\": \"^0.29.14\",\n \"jasonmccreary/laravel-test-assertions\": \"^2.5\",\n \"larastan/larastan\": \"^3.1\",\n \"maglnet/composer-require-checker\": \"^4.8\",\n \"mockery/mockery\": \"^1.6\",\n \"nunomaduro/collision\": \"^8.6\",\n \"phpstan/phpstan\": \"^2.1\",\n \"phpunit/phpunit\": \"^11.5.50\",\n \"symfony/phpunit-bridge\": \"^7.0\",\n \"vimeo/psalm\": \"^6.5.0\"\n },\n \"autoload\": {\n \"classmap\": [\n \"database\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\",\n \"Database\\\\Factories\\\\\": \"database/factories/\",\n \"Database\\\\Seeders\\\\\": \"database/seeders/\",\n \"Microsoft\\\\Graph\\\\Generated\\\\Models\\\\\": \"app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/\"\n },\n \"files\": [\n \"app/helpers.php\"\n ]\n },\n \"autoload-dev\": {\n \"classmap\": [\n \"tests/TestCase.php\"\n ],\n \"psr-4\": {\n \"Jiminny\\\\\": \"app/\",\n \"Tests\\\\\": \"tests/\"\n }\n },\n \"scripts\": {\n \"post-root-package-install\": [\n \"php -r \\\"file_exists('.env') || copy('.env.example', '.env');\\\"\"\n ],\n \"post-create-project-cmd\": [\n \"php artisan key:generate --ansi\"\n ],\n \"post-install-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postInstall\"\n ],\n \"post-update-cmd\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postUpdate\",\n \"php artisan ide-helper:generate\",\n \"php artisan ide-helper:meta\",\n \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\"\n ],\n \"post-autoload-dump\": [\n \"Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump\",\n \"@php artisan package:discover --ansi\"\n ]\n },\n \"config\": {\n \"preferred-install\": \"dist\",\n \"sort-packages\": true,\n \"optimize-autoloader\": true,\n \"allow-plugins\": {\n \"infection/extension-installer\": true,\n \"php-http/discovery\": true,\n \"tbachert/spi\": true\n }\n },\n \"extra\": {\n \"laravel\": {\n \"dont-discover\": [\n \"laravel/dusk\"\n ]\n },\n \"metasyntactical/composer-plugin-license-check\": {\n \"whitelist\": [],\n \"blacklist\": [\n \"AGPL\"\n ]\n }\n },\n \"repositories\": [\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/PHP-FFMpeg/BinaryDriver.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-salesloft.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-aircall.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-pipedrive.git\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-ringcentral\"\n },\n {\n \"type\": \"vcs\",\n \"url\": \"https://github.com/jiminny/oauth2-dialpad.git\"\n }\n ],\n \"prefer-stable\": true\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Install","depth":3,"bounds":{"left":0.90957445,"top":0.07821229,"width":0.013297873,"height":0.013567438},"help_text":"Installs packages from composer.json, taking account of composer.lock","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Update","depth":3,"bounds":{"left":0.9281915,"top":0.07821229,"width":0.016289894,"height":0.013567438},"help_text":"Installs latest appropriate versions of packages from composer.json","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Show log","depth":3,"bounds":{"left":0.94980055,"top":0.07821229,"width":0.020279255,"height":0.013567438},"help_text":"Show log of Composer-related actions","role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3155554097147706318
|
7935554484823069534
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
8
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Providers;
use GuzzleHttp\Client;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Jiminny\Component\BillingManagement\Denormalizer;
use Jiminny\Component\BillingManagement\MaxioClient;
use Jiminny\Component\BillingManagement\Repositories\ComponentMappingRepository;
use Jiminny\Integrations\Releases;
use Jiminny\Models\Account;
use Jiminny\Models\Activity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\Profile;
use Jiminny\Models\Lead;
use Jiminny\Models\SocialAccount;
use Jiminny\Observers\AccountObserver;
use Jiminny\Observers\ActivityObserver;
use Jiminny\Observers\ContactObserver;
use Jiminny\Observers\LeadObserver;
use Jiminny\Observers\ProfileObserver;
use Jiminny\Observers\SocialAccountObserver;
use Jiminny\Services\Internal\WebhookTokenValidator;
use Jiminny\Services\Internal\WebhookTokenGenerator;
use Jiminny\Services\Internal\WebhookForwarder;
use Laravel\Passport\Passport;
final class AppServiceProvider extends ServiceProvider
{
/**
* All the abilities that may be assigned to API tokens.
*/
private const array TOKENS_CAN = [
'dial-outbound' => 'Dial with the Softphone',
'start-conference' => 'Organize a Conference',
'sms' => 'Send & receive SMS with the Softphone',
'ghost-conference' => 'Join a Conference as a Ghost',
];
public function boot(): void
{
$this->app->bind(
MaxioClient::class,
fn () => new MaxioClient(
httpClient: new Client([
'base_uri' => config('maxio.api-route'),
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'auth' => [config('maxio.api-key'), config('maxio.password')],
]),
denormalizer: $this->app->get(Denormalizer::class),
componentMappingRepository: $this->app->get(ComponentMappingRepository::class),
),
);
$this->app->bind(
WebhookTokenValidator::class,
fn () => new WebhookTokenValidator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookTokenGenerator::class,
fn () => new WebhookTokenGenerator(
secret: config('services.internal_webhook.secret'),
),
);
$this->app->bind(
WebhookForwarder::class,
fn () => new WebhookForwarder(
tokenGenerator: $this->app->get(WebhookTokenGenerator::class),
httpClient: new Client(),
),
);
$this->app['router']->matched(function (RouteMatched $e) {
$route = $e->route;
if (! Arr::has($route->getAction(), 'guard')) {
return;
}
$routeGuard = Arr::get($route->getAction(), 'guard');
$this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
return $this->app['auth']->guard($routeGuard)->user();
});
$this->app['auth']->setDefaultDriver($routeGuard);
});
// Inject the controller name into the Body tag.
$this->app['view']->composer('layouts.header', function ($view) {
if (app('request')->route()) {
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
[$controller, $action] = explode('@', str_replace('Controller', '', $controller));
$view->with(compact('controller', 'action'));
} else {
$view->with(['controller' => null]);
}
});
$this->app->singleton(\Jiminny\Services\ActivityService::class);
Blade::directive('feature', function ($expression) {
return '<?php if (auth()->user() && auth()->user()->team->hasFeature(' . $expression . ')): ?>';
});
Blade::directive('endfeature', function ($expression) {
return '<?php endif; ?>';
});
Schema::defaultStringLength(191);
Passport::$clientUuids = false;
Passport::$registersJsonApiRoutes = true;
Passport::tokensCan(self::TOKENS_CAN);
Passport::withoutCookieSerialization();
SocialAccount::observe(SocialAccountObserver::class);
Profile::observe(ProfileObserver::class);
Activity::observe(ActivityObserver::class);
Lead::observe(LeadObserver::class);
Contact::observe(ContactObserver::class);
Account::observe(AccountObserver::class);
}
/**
* Register any application services.
*/
public function register(): void
{
if ($this->app->environment() === 'local') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
$this->registerEncryptCookies();
$this->registerReleases();
}
/**
* Register the releases class.
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
/**
* Register encrypt cookies.
*/
protected function registerEncryptCookies(): void
{
$this->app->singleton(\Jiminny\Http\Middleware\EncryptCookies::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
14
Previous Highlighted Error
Next Highlighted Error
{
"name": "jiminny/app",
"description": "The Jiminny Platform.",
"keywords": [
"training",
"salesforce",
"conference"
],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-date": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-igbinary": "*",
"ext-imagick": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mailparse": "*",
"ext-mbstring": "*",
"ext-mysqlnd": "*",
"ext-openssl": "*",
"ext-pcntl": "*",
"ext-pcre": "*",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-phar": "*",
"ext-phpiredis": "*",
"ext-posix": "*",
"ext-readline": "*",
"ext-redis": "*",
"ext-reflection": "*",
"ext-session": "*",
"ext-simplexml": "*",
"ext-sockets": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zend-opcache": "*",
"ext-zip": "*",
"ext-zlib": "*",
"lib-curl": "*",
"lib-curl-openssl": "*",
"lib-curl-zlib": "*",
"lib-date-timelib": "*",
"lib-date-zoneinfo": "*",
"lib-fileinfo-libmagic": "*",
"lib-gd": "*",
"lib-gd-freetype": "*",
"lib-gd-libjpeg": "*",
"lib-gd-libpng": "*",
"lib-gmp": "*",
"lib-icu": "*",
"lib-icu-cldr": "*",
"lib-icu-unicode": "*",
"lib-imagick-imagemagick": "*",
"lib-libxml": "*",
"lib-mbstring-libmbfl": "*",
"lib-mbstring-oniguruma": "*",
"lib-openssl": "*",
"lib-pcre": "*",
"lib-pcre-unicode": "*",
"lib-zip-libzip": "*",
"lib-zlib": "*",
"24slides/laravel-saml2": "^2.4",
"adam-paterson/oauth2-slack": "^1.1",
"asimlqt/php-google-spreadsheet-client": "^3.0",
"aws/aws-sdk-php": "^3.368",
"aws/aws-sdk-php-laravel": "^3.10",
"bepsvpt/secure-headers": "^9.0",
"chadhutchins/oauth2-slack": "^1.2",
"chaseconey/laravel-datadog-helper": "^1.2",
"chrisyue/php-m3u8": "4.0.3",
"daniti/oauth2-pipedrive": "dev-master",
"devio/pipedrive": "^2.6",
"doctrine/dbal": "^4.0",
"elasticsearch/elasticsearch": "^7.11",
"erusev/parsedown": "^1.7",
"fakerphp/faker": "^1.23",
"firebase/php-jwt": "^7.0",
"flipboxdigital/oauth2-hubspot": "1.0.1",
"giggsey/libphonenumber-for-php": "^8.12",
"google/apiclient": "^2.19",
"google/apiclient-services": "~0.360",
"google/apps-meet": "^0.5.1",
"guzzlehttp/guzzle": "^7.8",
"guzzlehttp/psr7": "^2.6",
"halaxa/json-machine": "^1.2",
"html2text/html2text": "^4.3",
"hubspot/api-client": "~5.0.0",
"hubspot/hubspot-php": "^5.2.0",
"intercom/intercom-php": "^4.5",
"intervention/image": "^3.4",
"jakeasmith/http_build_url": "^1.0",
"jdavidbakr/cloudfront-proxies": "^1.7",
"jeremykendall/php-domain-parser": "^6.3",
"jiminny/oauth2-aircall": "dev-master",
"jiminny/oauth2-bullhorn": "^0.2.0",
"jiminny/oauth2-dialpad": "dev-master",
"jiminny/oauth2-salesloft": "dev-master",
"jolicode/slack-php-api": "^4.5.0",
"kalnoy/nestedset": "*",
"laravel/framework": "^12.28",
"laravel/helpers": "^1.7",
"laravel/passport": "^13.0",
"laravel/slack-notification-channel": "^3.4",
"laravel/tinker": "^2.10.1",
"laravel/ui": "^4.6",
"laravolt/avatar": "^6.1",
"league/flysystem": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/fractal": "*",
"league/oauth2-client": "^2.7",
"league/oauth2-google": "^4.0",
"league/oauth2-linkedin": "^5.1",
"league/oauth2-server": "^9.2",
"league/statsd": "^2.0",
"markrogoyski/math-php": "^2.7.0",
"microsoft/microsoft-graph": "^2.51",
"monolog/monolog": "^3.0",
"nesbot/carbon": "^3.8",
"nette/caching": "*",
"phlib/sms-length": "^2.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"php-http/client-common": "^2.7",
"php-http/curl-client": "^2.3",
"php-http/httplug": "^2.2",
"php-http/message": "^1.16",
"phpseclib/phpseclib": "^3.0.36",
"propaganistas/laravel-phone": "^5.3",
"psr/cache": "^3.0",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"psr/simple-cache": "^3.0",
"pusher/pusher-php-server": "7.2.3",
"ramsey/uuid": "^4.2",
"ringcentral/ringcentral-php": "3.0.0",
"rmccue/requests": "^2.0",
"ruflin/elastica": "^7.1.1",
"santigarcor/laratrust": "^8.4",
"sentry/sentry": "4.13.0",
"sentry/sentry-laravel": "~4.13.0",
"shiftonelabs/laravel-sqs-fifo-queue": "^3.0",
"spatie/fractalistic": "^2.9",
"spatie/laravel-fractal": "^6.3",
"spatie/laravel-ignition": "^2.9",
"spatie/laravel-webhook-server": "^3.8",
"staudenmeir/belongs-to-through": "^2.17",
"stevenmaguire/oauth2-salesforce": "^2.0",
"symfony/cache": "^7.2",
"symfony/console": "^7.2",
"symfony/css-selector": "^7.2",
"symfony/debug": "^4.4",
"symfony/dom-crawler": "^7.2",
"symfony/expression-language": "^7.2",
"symfony/finder": "^7.2",
"symfony/http-client": "^7.3",
"symfony/http-foundation": "^7.2",
"symfony/http-kernel": "^7.2",
"symfony/postmark-mailer": "^7.3",
"symfony/process": "^7.3",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/var-dumper": "^7.2",
"symfony/workflow": "^7.2",
"tecnickcom/tcpdf": "^6.11",
"thenetworg/oauth2-azure": "dev-master",
"tmannherz/oauth2-ringcentral": "dev-master",
"twilio/sdk": "^8.3",
"vanderlee/php-sentence": "^1.0",
"vinkla/hashids": "^13.0",
"vlucas/phpdotenv": "^5.4",
"wildbit/postmark-php": "^6.0",
"willdurand/email-reply-parser": "^2.8",
"zbateson/mail-mime-parser": "^3.0.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"barryvdh/laravel-ide-helper": "^3.5",
"brianium/paratest": "^7.5",
"browserstack/browserstack-local": "^1.1.0",
"filp/whoops": "^2.9",
"friendsofphp/php-cs-fixer": "^3.66",
"infection/infection": "^0.29.14",
"jasonmccreary/laravel-test-assertions": "^2.5",
"larastan/larastan": "^3.1",
"maglnet/composer-require-checker": "^4.8",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5.50",
"symfony/phpunit-bridge": "^7.0",
"vimeo/psalm": "^6.5.0"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"Microsoft\\Graph\\Generated\\Models\\": "app/Services/MeetingGenerator/Overrides/Microsoft/Graph/Generated/Models/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
],
"psr-4": {
"Jiminny\\": "app/",
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate --ansi"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"infection/extension-installer": true,
"php-http/discovery": true,
"tbachert/spi": true
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
},
"metasyntactical/composer-plugin-license-check": {
"whitelist": [],
"blacklist": [
"AGPL"
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/PHP-FFMpeg/BinaryDriver.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-salesloft.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-aircall.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-pipedrive.git"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-ringcentral"
},
{
"type": "vcs",
"url": "https://github.com/jiminny/oauth2-dialpad.git"
}
],
"prefer-stable": true
}
Install
Update
Show log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
71963
|
1739
|
9
|
2026-04-22T14:35:23.918493+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776868523918_m2.jpg...
|
Firefox
|
Jiminny MCP Connector - Product - Confluence — Wor Jiminny MCP Connector - Product - Confluence — Work...
|
True
|
jiminny.atlassian.net/wiki/spaces/PROD/pages/37282 jiminny.atlassian.net/wiki/spaces/PROD/pages/3728244737/Jiminny+MCP+Connector...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny
Jiminny
Workers | Datadog
Workers | Datadog
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
CloudWatch | us-east-2
CloudWatch | us-east-2
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
Edit
You said what is the oauth required for mcp for clauide code and claude chat
You said
what is the oauth required for mcp for clauide code and claude chat
Listen
Show more options
Show thinking
Gemini said
Gemini said
To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.
MCP acts as a universal "USB port" that allows AI models to connect to external data sources and tools.
When connecting to remote, HTTP-based MCP servers, Anthropic relies on the
OAuth 2.1
authorization framework.
This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.
View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.
+2
Here is exactly what is required and how OAuth is handled across both environments.
The Underlying OAuth Flow for MCP
The Underlying OAuth Flow for MCP
Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.
The Claude application acts as the
OAuth Client
, and the remote tool you are connecting to acts as the
Resource Server
.
View source details for citation from Model Context Protocol. Opens side panel.
To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:
Initial Handshake (Discovery):
Claude attempts to connect to the MCP server without credentials. The server must reject this with a
401 Unauthorized
HTTP status and include a
WWW-Authenticate
header pointing to a Protected Resource Metadata (PRM) document.
Metadata Fetch:
Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).
View source details for citation from Model Context Protocol. Opens side panel.
User Consent:
Claude prompts the user to authenticate.
The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., "Allow Claude to access your GitHub repositories").
View source details for citations from GitHub and Model Context Protocol. Opens side panel.
+1
Token Exchange:
Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.
View source details for citation from Upstash. Opens side panel.
Access Granted:
Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.
View source details for citation from Model Context Protocol. Opens side panel.
MCP OAuth in Claude Code
MCP OAuth in Claude Code
Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:
Browser Handoff:
When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).
Authentication:
Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.
View source details for citation from Model Context Protocol. Opens side panel.
Alternative (Static Headers):
If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.
You can do this using the
--header
flag (e.g.,
claude mcp add my-server --transport http --header "Authorization: Bearer your-token"
).
View source details for citation from TrueFoundry. Opens side panel.
MCP OAuth in Claude Chat (Claude Desktop & Web)
MCP OAuth in Claude Chat (Claude Desktop & Web)
"Claude Chat" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.
Claude.ai (Web Custom Connectors):
When you add a remote MCP server via the "Custom Connectors" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.
Claude Desktop:
Claude Desktop traditionally relies on a local configuration file (
claude_desktop_config.json
). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.
When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.
It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your
config.json
free of sensitive plaintext passwords.
View source details for citation from GitHub. Opens side panel.
The Bottom Line:
You do not need to build a bespoke authentication protocol for MCP.
If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.
View source details for citation from GitHub. Opens side panel.
Sources
Sources
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to:
Top Bar
Top Bar
Sidebar
Sidebar
Main Content
Main Content
Collapse sidebar Ctrl [
Collapse sidebar
Ctrl
[
Switch sites or apps
Switch sites or apps
Confluence
Search, press enter to navigate to advanced search with your text query
Create
Create
Rovo Ask Rovo
Ask Rovo
Notifications
Notifications
Help
Help
[EMAIL]
[EMAIL]
For you
For you
Recent
Recent
Starred
Starred
Spaces
Spaces
Apps
Apps
Product
Product
More actions
More actions
Back to top
Back to top
Content
Content
Create Create
Create
Change view
Change view
Search by title
Results will update as you type.
Product Strategy 2025+ Change emoji
Product Strategy 2025+
Product Strategy 2025+
Competitive analysis Change emoji
Competitive analysis
Competitive analysis
Ways of working - Product processes Change emoji
Ways of working - Product processes
Ways of working - Product processes
Core Product Performance Metrics 📈
Core Product Performance Metrics
📈
Core Product Performance Metrics
Product Documentation Change emoji
Product Documentation
Product Documentation
Product Briefs Change emoji
Product Briefs
Product Briefs
Change emoji Activity Export
Activity Export
Change emoji Autologging activities
Autologging activities
Deal Insights - Multiple currencies
Deal Insights - Multiple currencies
Reinvent Themes & Topics (and ACS)
Reinvent Themes & Topics (and ACS)
Change emoji Billing Portal
Billing Portal
Change emoji Upload Video/Audio Recordings
Upload Video/Audio Recordings
White-Label Jiminny Instance
White-Label Jiminny Instance
Win/Loss Analysis for a Deal
Win/Loss Analysis for a Deal
Change emoji Hubspot app
Hubspot app
Change emoji Automatically hard delete data for churned customers
Automatically hard delete data for churned customers
Change emoji Ask Jiminny Anything on Call level
Ask Jiminny Anything on Call level
Change emoji Ask Jiminny for Open and Closed Deals
Ask Jiminny for Open and Closed Deals
Change emoji Ask Jiminny Anything on Deal level
Ask Jiminny Anything on Deal level
Change emoji Automatically record all calendar meetings
Automatically record all calendar meetings
Change emoji Product Tiering
Product Tiering
Change emoji Recording Consent
Recording Consent
Change emoji Automated CRM Filling
Automated CRM Filling
Change emoji Automated Exec Reports
Automated Exec Reports
Change emoji Auto-detect Activity Type
Auto-detect Activity Type
AI Signals & Alerts
AI Signals & Alerts
Change emoji AJA on Anything
AJA on Anything
Change emoji AI Call Scoring
AI Call Scoring
Jiminny MCP Connector
Jiminny MCP Connector
Desktop app to record a meetings without visible Notetaker
Desktop app to record a meetings without visible Notetaker
Feedback Change emoji
Feedback
Feedback
Research & User Feedback Change emoji
Research & User Feedback
Research & User Feedback
Create
Create
Jira , (opens new window)
Jira
, (opens new window)
Teams , (opens new window)
Teams
, (opens new window)
open menu
open menu
More
More
Side Navigation Drag Handle
Breadcrumbs
Jiminny MCP Connector
Updated 10m ago
Updated 10m ago
Edit this content
Edit
Share, Open - Anyone in the space can edit
Share
Copy link
Copy link
More actions
More actions
Jiminny MCP Connector
Jiminny MCP Connector
Jiminny MCP Connector
By Galya Dimitrova
By Galya Dimitrova
Read time 12 min
12 min
Views 14
14
Add a reaction
Add a reaction
Epic
Link to Epik in Jira...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.28307846,"top":0.0518755,"width":0.07596409,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"SevenShores\\Hubspot\\Exceptions\\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {\"status\":\"error\",\"message\":\"You have reached your secondly limit.\",\"errorType\":\"RATE_LIMIT","depth":4,"bounds":{"left":0.28125,"top":0.09497207,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SevenShores\\Hubspot\\Exceptions\\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {\"status\":\"error\",\"message\":\"You have reached your secondly limit.\",\"errorType\":\"RATE_LIMIT","depth":5,"bounds":{"left":0.2945479,"top":0.10614525,"width":0.4644282,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":4,"bounds":{"left":0.28125,"top":0.12769353,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":5,"bounds":{"left":0.2945479,"top":0.13886672,"width":0.11319814,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny MCP Connector - Product - Confluence","depth":4,"bounds":{"left":0.28125,"top":0.16041501,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny MCP Connector - Product - Confluence","depth":5,"bounds":{"left":0.2945479,"top":0.17158818,"width":0.08294548,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.28125,"top":0.19313647,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.2945479,"top":0.20430966,"width":0.013131649,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Workers | Datadog","depth":4,"bounds":{"left":0.28125,"top":0.22585794,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Workers | Datadog","depth":5,"bounds":{"left":0.2945479,"top":0.23703113,"width":0.032081116,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.28125,"top":0.2585794,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.2945479,"top":0.2697526,"width":0.04537899,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira","depth":4,"bounds":{"left":0.28125,"top":0.29130086,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira","depth":5,"bounds":{"left":0.2945479,"top":0.30247405,"width":0.15791224,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":4,"bounds":{"left":0.28125,"top":0.32402235,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":5,"bounds":{"left":0.2945479,"top":0.33519554,"width":0.16555852,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.28125,"top":0.3567438,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.2945479,"top":0.367917,"width":0.013131649,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"CloudWatch | us-east-2","depth":4,"bounds":{"left":0.28125,"top":0.38946527,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CloudWatch | us-east-2","depth":5,"bounds":{"left":0.2945479,"top":0.40063846,"width":0.041223403,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Your 'Not enpough activities' report wasn't generated - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.28125,"top":0.42218676,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your 'Not enpough activities' report wasn't generated - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.2945479,"top":0.43335995,"width":0.16821809,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny MCP Connector - Product - Confluence","depth":4,"bounds":{"left":0.28125,"top":0.45490822,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jiminny MCP Connector - Product - Confluence","depth":5,"bounds":{"left":0.2945479,"top":0.4660814,"width":0.08294548,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.34857047,"top":0.46209097,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.2840758,"top":0.48922586,"width":0.07413564,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.2840758,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.29504654,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.30618352,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.31732047,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.32845744,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.4659242,"top":0.055067837,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.47789228,"top":0.055067837,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.47523272,"top":0.103751,"width":0.013297873,"height":0.031923383},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.3648604,"top":0.103751,"width":0.013297873,"height":0.031923383},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.44730717,"top":0.103751,"width":0.013297873,"height":0.031923383},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.46060506,"top":0.103751,"width":0.013297873,"height":0.031923383},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.36053857,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.36053857,"top":0.15003991,"width":0.1200133,"height":0.025538707},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said what is the oauth required for mcp for clauide code and claude chat","depth":21,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"what is the oauth required for mcp for clauide code and claude chat","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP acts as a universal \"USB port\" that allows AI models to connect to external data sources and tools.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When connecting to remote, HTTP-based MCP servers, Anthropic relies on the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"OAuth 2.1","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"authorization framework.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"+2","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is exactly what is required and how OAuth is handled across both environments.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"The Underlying OAuth Flow for MCP","depth":23,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Underlying OAuth Flow for MCP","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Claude application acts as the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"OAuth Client","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and the remote tool you are connecting to acts as the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Resource Server","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:","depth":24,"bounds":{"left":0.3695146,"top":0.0,"width":0.11087101,"height":0.057861134},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Initial Handshake (Discovery):","depth":26,"bounds":{"left":0.38148272,"top":0.0,"width":0.07829122,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude attempts to connect to the MCP server without credentials. The server must reject this with a","depth":26,"bounds":{"left":0.38148272,"top":0.0,"width":0.1043883,"height":0.07861133},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"401 Unauthorized","depth":27,"bounds":{"left":0.40940824,"top":0.035115723,"width":0.044714097,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"HTTP status and include a","depth":26,"bounds":{"left":0.38148272,"top":0.033918597,"width":0.09042553,"height":0.037110932},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"WWW-Authenticate","depth":27,"bounds":{"left":0.43334442,"top":0.05586592,"width":0.044714097,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"header pointing to a Protected Resource Metadata (PRM) document.","depth":26,"bounds":{"left":0.38148272,"top":0.054668795,"width":0.09990027,"height":0.057861134},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Metadata Fetch:","depth":26,"bounds":{"left":0.38148272,"top":0.12330407,"width":0.042220745,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).","depth":26,"bounds":{"left":0.38148272,"top":0.12330407,"width":0.09840426,"height":0.07861133},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"bounds":{"left":0.45960772,"top":0.1859537,"width":0.008643617,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"User Consent:","depth":26,"bounds":{"left":0.38148272,"top":0.21268955,"width":0.036402926,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude prompts the user to authenticate.","depth":26,"bounds":{"left":0.38148272,"top":0.21268955,"width":0.1043883,"height":0.037110932},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., \"Allow Claude to access your GitHub repositories\").","depth":26,"bounds":{"left":0.38148272,"top":0.23343974,"width":0.1043883,"height":0.09936153},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citations from GitHub and Model Context Protocol. Opens side panel.","depth":26,"bounds":{"left":0.4175532,"top":0.31683958,"width":0.013796543,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"+1","depth":28,"bounds":{"left":0.4242021,"top":0.31883478,"width":0.0038231383,"height":0.012370312},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Token Exchange:","depth":26,"bounds":{"left":0.38148272,"top":0.34357542,"width":0.043716755,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.","depth":26,"bounds":{"left":0.38148272,"top":0.34357542,"width":0.099734046,"height":0.07861133},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Upstash. Opens side panel.","depth":26,"bounds":{"left":0.44198802,"top":0.40622506,"width":0.008643617,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Access Granted:","depth":26,"bounds":{"left":0.38148272,"top":0.4329609,"width":0.04255319,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.","depth":26,"bounds":{"left":0.38148272,"top":0.4329609,"width":0.1043883,"height":0.07861133},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"bounds":{"left":0.4119016,"top":0.49561054,"width":0.008643617,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"MCP OAuth in Claude Code","depth":23,"bounds":{"left":0.3695146,"top":0.5494813,"width":0.11635638,"height":0.01915403},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP OAuth in Claude Code","depth":24,"bounds":{"left":0.3695146,"top":0.5510774,"width":0.07047872,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:","depth":24,"bounds":{"left":0.3695146,"top":0.5774142,"width":0.11469415,"height":0.09936153},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browser Handoff:","depth":26,"bounds":{"left":0.38115028,"top":0.6875499,"width":0.04537899,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).","depth":26,"bounds":{"left":0.38115028,"top":0.6875499,"width":0.1043883,"height":0.12011173},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication:","depth":26,"bounds":{"left":0.38115028,"top":0.8184357,"width":0.040226065,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.","depth":26,"bounds":{"left":0.38115028,"top":0.8184357,"width":0.10472074,"height":0.12011173},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"bounds":{"left":0.39660904,"top":0.9225858,"width":0.008643617,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Alternative (Static Headers):","depth":26,"bounds":{"left":0.38115028,"top":0.9493216,"width":0.07430186,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.","depth":26,"bounds":{"left":0.38115028,"top":0.9493216,"width":0.10472074,"height":0.050678372},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You can do this using the","depth":26,"bounds":{"left":0.38115028,"top":1.0,"width":0.09690824,"height":-0.032322407},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"--header","depth":27,"bounds":{"left":0.44481382,"top":1.0,"width":0.022273935,"height":-0.05426979},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"flag (e.g.,","depth":26,"bounds":{"left":0.38115028,"top":1.0,"width":0.09990027,"height":-0.05307257},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"claude mcp add my-server --transport http --header \"Authorization: Bearer your-token\"","depth":27,"bounds":{"left":0.38115028,"top":1.0,"width":0.094913565,"height":-0.075019956},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from TrueFoundry. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"MCP OAuth in Claude Chat (Claude Desktop & Web)","depth":23,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP OAuth in Claude Chat (Claude Desktop & Web)","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\"Claude Chat\" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude.ai (Web Custom Connectors):","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When you add a remote MCP server via the \"Custom Connectors\" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Desktop:","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Desktop traditionally relies on a local configuration file (","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"claude_desktop_config.json","depth":27,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"config.json","depth":27,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"free of sensitive plaintext passwords.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from GitHub. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"The Bottom Line:","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You do not need to build a bespoke authentication protocol for MCP.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from GitHub. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Sources","depth":23,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sources","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextArea","text":"Enter a prompt for Gemini\nencrypted","depth":20,"bounds":{"left":0.37416887,"top":0.8104549,"width":0.10638298,"height":0.01915403},"value":"Enter a prompt for Gemini\nencrypted","help_text":"","role_description":"text entry area","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enter a prompt for Gemini","depth":21,"bounds":{"left":0.38081783,"top":0.81085396,"width":0.069980055,"height":0.018355945},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"encrypted","depth":21,"bounds":{"left":0.37317154,"top":0.8104549,"width":0.0066489363,"height":0.01915403},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open upload file menu","depth":20,"bounds":{"left":0.37017953,"top":0.8447725,"width":0.013297873,"height":0.031923383},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tools","depth":18,"bounds":{"left":0.38613698,"top":0.8447725,"width":0.013297873,"height":0.031923383},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open mode picker","depth":20,"bounds":{"left":0.4431516,"top":0.8439745,"width":0.026097074,"height":0.031923383},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pro","depth":23,"bounds":{"left":0.44847074,"top":0.8527534,"width":0.007480053,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Microphone","depth":19,"bounds":{"left":0.47124335,"top":0.8439745,"width":0.013297873,"height":0.031923383},"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.","depth":17,"bounds":{"left":0.3665226,"top":0.896249,"width":0.12167553,"height":0.025139665},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Your privacy & Gemini Opens in a new window","depth":17,"bounds":{"left":0.40724733,"top":0.92178774,"width":0.040226065,"height":0.012370312},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your privacy & Gemini","depth":18,"bounds":{"left":0.40724733,"top":0.92178774,"width":0.040226065,"height":0.012370312},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Opens in a new window","depth":19,"bounds":{"left":0.36053857,"top":0.92098963,"width":0.043218084,"height":0.012370312},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Summarize page","depth":7,"bounds":{"left":0.36619017,"top":0.95730245,"width":0.053523935,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Summarize page","depth":9,"bounds":{"left":0.37184176,"top":0.96249,"width":0.042220745,"height":0.015163607},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Skip to:","depth":10,"bounds":{"left":0.50731385,"top":0.07861133,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Top Bar","depth":11,"bounds":{"left":0.50731385,"top":0.097765364,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Top Bar","depth":12,"bounds":{"left":0.50731385,"top":0.097765364,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Sidebar","depth":11,"bounds":{"left":0.50731385,"top":0.11691939,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sidebar","depth":12,"bounds":{"left":0.50731385,"top":0.11691939,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Main Content","depth":11,"bounds":{"left":0.50731385,"top":0.13607343,"width":0.029421542,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Main Content","depth":12,"bounds":{"left":0.50731385,"top":0.13607343,"width":0.029421542,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Collapse sidebar Ctrl [","depth":10,"bounds":{"left":0.5006649,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Collapse sidebar","depth":12,"bounds":{"left":0.50581783,"top":0.066640064,"width":0.03673537,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Ctrl","depth":13,"bounds":{"left":0.5465425,"top":0.066640064,"width":0.007978723,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[","depth":13,"bounds":{"left":0.5611702,"top":0.066640064,"width":0.0016622341,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Switch sites or apps","depth":12,"bounds":{"left":0.51263297,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Switch sites or apps","depth":14,"bounds":{"left":0.5177859,"top":0.06344773,"width":0.044215426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Confluence","depth":10,"bounds":{"left":0.5259308,"top":0.057861134,"width":0.029421542,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXComboBox","text":"Search, press enter to navigate to advanced search with your text query","depth":11,"bounds":{"left":0.62400264,"top":0.06264964,"width":0.22140957,"height":0.015961692},"help_text":"","placeholder":"Search Confluence, Jira, Google Drive and other apps","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Create","depth":10,"bounds":{"left":0.8537234,"top":0.057861134,"width":0.030086435,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create","depth":12,"bounds":{"left":0.8650266,"top":0.06384677,"width":0.014793883,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Rovo Ask Rovo","depth":13,"bounds":{"left":0.92420214,"top":0.057861134,"width":0.036070477,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Rovo","depth":15,"bounds":{"left":0.93550533,"top":0.06384677,"width":0.020777926,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Notifications","depth":13,"bounds":{"left":0.9616024,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Notifications","depth":15,"bounds":{"left":0.96675533,"top":0.06344773,"width":0.027759308,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Help","depth":13,"bounds":{"left":0.97357047,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Help","depth":15,"bounds":{"left":0.9787234,"top":0.06344773,"width":0.010139627,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"lukas.kovalik@jiminny.com","depth":13,"bounds":{"left":0.98553854,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lukas.kovalik@jiminny.com","depth":15,"bounds":{"left":0.9906915,"top":0.06344773,"width":0.009308517,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"For you","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"For you","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Recent","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Recent","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Starred","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Starred","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Spaces","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Spaces","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Apps","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Apps","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product","depth":13,"bounds":{"left":0.5006649,"top":0.09976058,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product","depth":16,"bounds":{"left":0.5113032,"top":0.10574621,"width":0.017453458,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions","depth":14,"bounds":{"left":0.6452792,"top":0.10295291,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Back to top","depth":11,"bounds":{"left":0.5568484,"top":0.1396648,"width":0.04155585,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Back to top","depth":13,"bounds":{"left":0.5696476,"top":0.14644852,"width":0.021276595,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Content","depth":12,"bounds":{"left":0.5006649,"top":0.0,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Content","depth":15,"bounds":{"left":0.5113032,"top":0.0,"width":0.01761968,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Create Create","depth":14,"bounds":{"left":0.63730055,"top":0.0,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Create","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Change view","depth":13,"bounds":{"left":0.6452792,"top":0.0,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Change view","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Search by title","depth":15,"bounds":{"left":0.51163566,"top":0.005586592,"width":0.14261968,"height":0.022346368},"role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Results will update as you type.","depth":15,"bounds":{"left":0.50332445,"top":0.00518755,"width":0.06931516,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Strategy 2025+ Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.035913806,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Strategy 2025+","depth":17,"bounds":{"left":0.50598407,"top":0.039106146,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Product Strategy 2025+","depth":19,"bounds":{"left":0.52393615,"top":0.041899443,"width":0.053523935,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Competitive analysis Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.061452515,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Competitive analysis","depth":17,"bounds":{"left":0.50598407,"top":0.06464485,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Competitive analysis","depth":19,"bounds":{"left":0.52393615,"top":0.06743815,"width":0.046210106,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Ways of working - Product processes Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.08699122,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Ways of working - Product processes","depth":17,"bounds":{"left":0.50598407,"top":0.090183556,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Ways of working - Product processes","depth":19,"bounds":{"left":0.52393615,"top":0.09297685,"width":0.08361037,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Core Product Performance Metrics 📈","depth":16,"bounds":{"left":0.5046542,"top":0.112529926,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Core Product Performance Metrics","depth":17,"bounds":{"left":0.50598407,"top":0.11572227,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"📈","depth":18,"bounds":{"left":0.5159575,"top":0.118515566,"width":0.004654255,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Core Product Performance Metrics","depth":19,"bounds":{"left":0.52393615,"top":0.118515566,"width":0.0774601,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Documentation Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.13806863,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Documentation","depth":17,"bounds":{"left":0.50598407,"top":0.14126097,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Product Documentation","depth":19,"bounds":{"left":0.52393615,"top":0.14405426,"width":0.05269282,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Briefs Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.16360734,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Briefs","depth":17,"bounds":{"left":0.50598407,"top":0.16679968,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Product Briefs","depth":19,"bounds":{"left":0.52393615,"top":0.16959298,"width":0.03174867,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Activity Export","depth":18,"bounds":{"left":0.5099734,"top":0.18914606,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Activity Export","depth":21,"bounds":{"left":0.52925533,"top":0.19513169,"width":0.03274601,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Autologging activities","depth":18,"bounds":{"left":0.5099734,"top":0.21468475,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Autologging activities","depth":21,"bounds":{"left":0.52925533,"top":0.22067039,"width":0.048204787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Deal Insights - Multiple currencies","depth":18,"bounds":{"left":0.5099734,"top":0.24022347,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Deal Insights - Multiple currencies","depth":21,"bounds":{"left":0.52925533,"top":0.2462091,"width":0.076961435,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Reinvent Themes & Topics (and ACS)","depth":18,"bounds":{"left":0.5099734,"top":0.26576218,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Reinvent Themes & Topics (and ACS)","depth":21,"bounds":{"left":0.52925533,"top":0.2717478,"width":0.082446806,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Billing Portal","depth":18,"bounds":{"left":0.5099734,"top":0.29130086,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Billing Portal","depth":21,"bounds":{"left":0.52925533,"top":0.2972865,"width":0.027759308,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Upload Video/Audio Recordings","depth":18,"bounds":{"left":0.5099734,"top":0.31683958,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Upload Video/Audio Recordings","depth":21,"bounds":{"left":0.52925533,"top":0.32282522,"width":0.0709774,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"White-Label Jiminny Instance","depth":18,"bounds":{"left":0.5099734,"top":0.3423783,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"White-Label Jiminny Instance","depth":21,"bounds":{"left":0.52925533,"top":0.34836394,"width":0.066821806,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Win/Loss Analysis for a Deal","depth":18,"bounds":{"left":0.5099734,"top":0.367917,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Win/Loss Analysis for a Deal","depth":21,"bounds":{"left":0.52925533,"top":0.37390262,"width":0.06349734,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Hubspot app","depth":18,"bounds":{"left":0.5099734,"top":0.3934557,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Hubspot app","depth":21,"bounds":{"left":0.52925533,"top":0.39944133,"width":0.028590426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automatically hard delete data for churned customers","depth":18,"bounds":{"left":0.5099734,"top":0.41899443,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automatically hard delete data for churned customers","depth":21,"bounds":{"left":0.52925533,"top":0.42498004,"width":0.11968085,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny Anything on Call level","depth":18,"bounds":{"left":0.5099734,"top":0.4445331,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny Anything on Call level","depth":21,"bounds":{"left":0.52925533,"top":0.45051876,"width":0.076296546,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny for Open and Closed Deals","depth":18,"bounds":{"left":0.5099734,"top":0.47007182,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny for Open and Closed Deals","depth":21,"bounds":{"left":0.52925533,"top":0.47605747,"width":0.087765954,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny Anything on Deal level","depth":18,"bounds":{"left":0.5099734,"top":0.49561054,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny Anything on Deal level","depth":21,"bounds":{"left":0.52925533,"top":0.50159615,"width":0.077792555,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automatically record all calendar meetings","depth":18,"bounds":{"left":0.5099734,"top":0.5211492,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automatically record all calendar meetings","depth":21,"bounds":{"left":0.52925533,"top":0.5271349,"width":0.094913565,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Product Tiering","depth":18,"bounds":{"left":0.5099734,"top":0.54668796,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product Tiering","depth":21,"bounds":{"left":0.52925533,"top":0.5526736,"width":0.034408245,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Recording Consent","depth":18,"bounds":{"left":0.5099734,"top":0.57222664,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recording Consent","depth":21,"bounds":{"left":0.52925533,"top":0.57821226,"width":0.04255319,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automated CRM Filling","depth":18,"bounds":{"left":0.5099734,"top":0.5977654,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automated CRM Filling","depth":21,"bounds":{"left":0.52925533,"top":0.603751,"width":0.05069814,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automated Exec Reports","depth":18,"bounds":{"left":0.5099734,"top":0.62330407,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automated Exec Reports","depth":21,"bounds":{"left":0.52925533,"top":0.6292897,"width":0.054853722,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Auto-detect Activity Type","depth":18,"bounds":{"left":0.5099734,"top":0.64884275,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Auto-detect Activity Type","depth":21,"bounds":{"left":0.52925533,"top":0.6548284,"width":0.05718085,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"AI Signals & Alerts","depth":18,"bounds":{"left":0.5099734,"top":0.6743815,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI Signals & Alerts","depth":21,"bounds":{"left":0.52925533,"top":0.6803671,"width":0.04155585,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji AJA on Anything","depth":18,"bounds":{"left":0.5099734,"top":0.6999202,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AJA on Anything","depth":21,"bounds":{"left":0.52925533,"top":0.70590585,"width":0.03723404,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji AI Call Scoring","depth":18,"bounds":{"left":0.5099734,"top":0.7254589,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI Call Scoring","depth":21,"bounds":{"left":0.52925533,"top":0.73144454,"width":0.03357713,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jiminny MCP Connector","depth":18,"bounds":{"left":0.5099734,"top":0.7509976,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":21,"bounds":{"left":0.52925533,"top":0.7569832,"width":0.053856384,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop app to record a meetings without visible Notetaker","depth":18,"bounds":{"left":0.5099734,"top":0.7765363,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop app to record a meetings without visible Notetaker","depth":21,"bounds":{"left":0.52925533,"top":0.78252196,"width":0.13164894,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Feedback Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.802075,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Feedback","depth":17,"bounds":{"left":0.50598407,"top":0.80526733,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Feedback","depth":19,"bounds":{"left":0.52393615,"top":0.80806065,"width":0.021775266,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Research & User Feedback Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.8276137,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Research & User Feedback","depth":17,"bounds":{"left":0.50598407,"top":0.8308061,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Research & User Feedback","depth":19,"bounds":{"left":0.52393615,"top":0.8335994,"width":0.059840426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create","depth":16,"bounds":{"left":0.5046542,"top":0.85315245,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create","depth":19,"bounds":{"left":0.5152925,"top":0.8591381,"width":0.014793883,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jira , (opens new window)","depth":14,"bounds":{"left":0.5006649,"top":0.9042298,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jira","depth":18,"bounds":{"left":0.5113032,"top":0.9102155,"width":0.00831117,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", (opens new window)","depth":16,"bounds":{"left":0.5006649,"top":0.91779727,"width":0.04837101,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Teams , (opens new window)","depth":14,"bounds":{"left":0.5006649,"top":0.92976856,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Teams","depth":18,"bounds":{"left":0.5113032,"top":0.9357542,"width":0.014793883,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", (opens new window)","depth":16,"bounds":{"left":0.5006649,"top":0.943336,"width":0.04837101,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"open menu","depth":15,"bounds":{"left":0.64328456,"top":0.93296087,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"open menu","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More","depth":13,"bounds":{"left":0.5006649,"top":0.9648843,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":16,"bounds":{"left":0.5113032,"top":0.9708699,"width":0.011469414,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Side Navigation Drag Handle","depth":14,"bounds":{"left":0.71043885,"top":0.0981644,"width":0.0631649,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Breadcrumbs","depth":15,"bounds":{"left":0.66289896,"top":0.09936153,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":17,"bounds":{"left":0.68417555,"top":0.105347164,"width":0.0546875,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Updated 10m ago","depth":16,"bounds":{"left":0.8374335,"top":0.105347164,"width":0.03873005,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Updated 10m ago","depth":17,"bounds":{"left":0.8374335,"top":0.105347164,"width":0.03873005,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Edit this content","depth":16,"bounds":{"left":0.91738695,"top":0.09936153,"width":0.023603724,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Edit","depth":18,"bounds":{"left":0.92869014,"top":0.105347164,"width":0.00831117,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Share, Open - Anyone in the space can edit","depth":16,"bounds":{"left":0.94298536,"top":0.09936153,"width":0.02825798,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Share","depth":18,"bounds":{"left":0.95428854,"top":0.105347164,"width":0.012965426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy link","depth":16,"bounds":{"left":0.9709109,"top":0.09936153,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Copy link","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions","depth":15,"bounds":{"left":0.9835439,"top":0.09936153,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Jiminny MCP Connector","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Jiminny MCP Connector","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"By Galya Dimitrova","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"By Galya Dimitrova","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Read time 12 min","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"12 min","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Views 14","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"14","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Add a reaction","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Add a reaction","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Epic","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Link to Epik in Jira","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
6068698830231227724
|
7934688911726433014
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny
Jiminny
Workers | Datadog
Workers | Datadog
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
CloudWatch | us-east-2
CloudWatch | us-east-2
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
Edit
You said what is the oauth required for mcp for clauide code and claude chat
You said
what is the oauth required for mcp for clauide code and claude chat
Listen
Show more options
Show thinking
Gemini said
Gemini said
To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.
MCP acts as a universal "USB port" that allows AI models to connect to external data sources and tools.
When connecting to remote, HTTP-based MCP servers, Anthropic relies on the
OAuth 2.1
authorization framework.
This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.
View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.
+2
Here is exactly what is required and how OAuth is handled across both environments.
The Underlying OAuth Flow for MCP
The Underlying OAuth Flow for MCP
Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.
The Claude application acts as the
OAuth Client
, and the remote tool you are connecting to acts as the
Resource Server
.
View source details for citation from Model Context Protocol. Opens side panel.
To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:
Initial Handshake (Discovery):
Claude attempts to connect to the MCP server without credentials. The server must reject this with a
401 Unauthorized
HTTP status and include a
WWW-Authenticate
header pointing to a Protected Resource Metadata (PRM) document.
Metadata Fetch:
Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).
View source details for citation from Model Context Protocol. Opens side panel.
User Consent:
Claude prompts the user to authenticate.
The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., "Allow Claude to access your GitHub repositories").
View source details for citations from GitHub and Model Context Protocol. Opens side panel.
+1
Token Exchange:
Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.
View source details for citation from Upstash. Opens side panel.
Access Granted:
Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.
View source details for citation from Model Context Protocol. Opens side panel.
MCP OAuth in Claude Code
MCP OAuth in Claude Code
Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:
Browser Handoff:
When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).
Authentication:
Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.
View source details for citation from Model Context Protocol. Opens side panel.
Alternative (Static Headers):
If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.
You can do this using the
--header
flag (e.g.,
claude mcp add my-server --transport http --header "Authorization: Bearer your-token"
).
View source details for citation from TrueFoundry. Opens side panel.
MCP OAuth in Claude Chat (Claude Desktop & Web)
MCP OAuth in Claude Chat (Claude Desktop & Web)
"Claude Chat" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.
Claude.ai (Web Custom Connectors):
When you add a remote MCP server via the "Custom Connectors" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.
Claude Desktop:
Claude Desktop traditionally relies on a local configuration file (
claude_desktop_config.json
). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.
When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.
It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your
config.json
free of sensitive plaintext passwords.
View source details for citation from GitHub. Opens side panel.
The Bottom Line:
You do not need to build a bespoke authentication protocol for MCP.
If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.
View source details for citation from GitHub. Opens side panel.
Sources
Sources
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to:
Top Bar
Top Bar
Sidebar
Sidebar
Main Content
Main Content
Collapse sidebar Ctrl [
Collapse sidebar
Ctrl
[
Switch sites or apps
Switch sites or apps
Confluence
Search, press enter to navigate to advanced search with your text query
Create
Create
Rovo Ask Rovo
Ask Rovo
Notifications
Notifications
Help
Help
[EMAIL]
[EMAIL]
For you
For you
Recent
Recent
Starred
Starred
Spaces
Spaces
Apps
Apps
Product
Product
More actions
More actions
Back to top
Back to top
Content
Content
Create Create
Create
Change view
Change view
Search by title
Results will update as you type.
Product Strategy 2025+ Change emoji
Product Strategy 2025+
Product Strategy 2025+
Competitive analysis Change emoji
Competitive analysis
Competitive analysis
Ways of working - Product processes Change emoji
Ways of working - Product processes
Ways of working - Product processes
Core Product Performance Metrics 📈
Core Product Performance Metrics
📈
Core Product Performance Metrics
Product Documentation Change emoji
Product Documentation
Product Documentation
Product Briefs Change emoji
Product Briefs
Product Briefs
Change emoji Activity Export
Activity Export
Change emoji Autologging activities
Autologging activities
Deal Insights - Multiple currencies
Deal Insights - Multiple currencies
Reinvent Themes & Topics (and ACS)
Reinvent Themes & Topics (and ACS)
Change emoji Billing Portal
Billing Portal
Change emoji Upload Video/Audio Recordings
Upload Video/Audio Recordings
White-Label Jiminny Instance
White-Label Jiminny Instance
Win/Loss Analysis for a Deal
Win/Loss Analysis for a Deal
Change emoji Hubspot app
Hubspot app
Change emoji Automatically hard delete data for churned customers
Automatically hard delete data for churned customers
Change emoji Ask Jiminny Anything on Call level
Ask Jiminny Anything on Call level
Change emoji Ask Jiminny for Open and Closed Deals
Ask Jiminny for Open and Closed Deals
Change emoji Ask Jiminny Anything on Deal level
Ask Jiminny Anything on Deal level
Change emoji Automatically record all calendar meetings
Automatically record all calendar meetings
Change emoji Product Tiering
Product Tiering
Change emoji Recording Consent
Recording Consent
Change emoji Automated CRM Filling
Automated CRM Filling
Change emoji Automated Exec Reports
Automated Exec Reports
Change emoji Auto-detect Activity Type
Auto-detect Activity Type
AI Signals & Alerts
AI Signals & Alerts
Change emoji AJA on Anything
AJA on Anything
Change emoji AI Call Scoring
AI Call Scoring
Jiminny MCP Connector
Jiminny MCP Connector
Desktop app to record a meetings without visible Notetaker
Desktop app to record a meetings without visible Notetaker
Feedback Change emoji
Feedback
Feedback
Research & User Feedback Change emoji
Research & User Feedback
Research & User Feedback
Create
Create
Jira , (opens new window)
Jira
, (opens new window)
Teams , (opens new window)
Teams
, (opens new window)
open menu
open menu
More
More
Side Navigation Drag Handle
Breadcrumbs
Jiminny MCP Connector
Updated 10m ago
Updated 10m ago
Edit this content
Edit
Share, Open - Anyone in the space can edit
Share
Copy link
Copy link
More actions
More actions
Jiminny MCP Connector
Jiminny MCP Connector
Jiminny MCP Connector
By Galya Dimitrova
By Galya Dimitrova
Read time 12 min
12 min
Views 14
14
Add a reaction
Add a reaction
Epic
Link to Epik in Jira...
|
NULL
|
|
71972
|
1739
|
14
|
2026-04-22T14:35:56.217639+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776868556217_m2.jpg...
|
Firefox
|
Jiminny MCP Connector - Product - Confluence — Wor Jiminny MCP Connector - Product - Confluence — Work...
|
True
|
jiminny.atlassian.net/wiki/spaces/PROD/pages/37282 jiminny.atlassian.net/wiki/spaces/PROD/pages/3728244737/Jiminny+MCP+Connector...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny
Jiminny
Workers | Datadog
Workers | Datadog
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
CloudWatch | us-east-2
CloudWatch | us-east-2
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
Edit
You said what is the oauth required for mcp for clauide code and claude chat
You said
what is the oauth required for mcp for clauide code and claude chat
Listen
Show more options
Show thinking
Gemini said
Gemini said
To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.
MCP acts as a universal "USB port" that allows AI models to connect to external data sources and tools.
When connecting to remote, HTTP-based MCP servers, Anthropic relies on the
OAuth 2.1
authorization framework.
This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.
View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.
+2
Here is exactly what is required and how OAuth is handled across both environments.
The Underlying OAuth Flow for MCP
The Underlying OAuth Flow for MCP
Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.
The Claude application acts as the
OAuth Client
, and the remote tool you are connecting to acts as the
Resource Server
.
View source details for citation from Model Context Protocol. Opens side panel.
To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:
Initial Handshake (Discovery):
Claude attempts to connect to the MCP server without credentials. The server must reject this with a
401 Unauthorized
HTTP status and include a
WWW-Authenticate
header pointing to a Protected Resource Metadata (PRM) document.
Metadata Fetch:
Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).
View source details for citation from Model Context Protocol. Opens side panel.
User Consent:
Claude prompts the user to authenticate.
The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., "Allow Claude to access your GitHub repositories").
View source details for citations from GitHub and Model Context Protocol. Opens side panel.
+1
Token Exchange:
Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.
View source details for citation from Upstash. Opens side panel.
Access Granted:
Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.
View source details for citation from Model Context Protocol. Opens side panel.
MCP OAuth in Claude Code
MCP OAuth in Claude Code
Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:
Browser Handoff:
When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).
Authentication:
Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.
View source details for citation from Model Context Protocol. Opens side panel.
Alternative (Static Headers):
If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.
You can do this using the
--header
flag (e.g.,
claude mcp add my-server --transport http --header "Authorization: Bearer your-token"
).
View source details for citation from TrueFoundry. Opens side panel.
MCP OAuth in Claude Chat (Claude Desktop & Web)
MCP OAuth in Claude Chat (Claude Desktop & Web)
"Claude Chat" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.
Claude.ai (Web Custom Connectors):
When you add a remote MCP server via the "Custom Connectors" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.
Claude Desktop:
Claude Desktop traditionally relies on a local configuration file (
claude_desktop_config.json
). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.
When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.
It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your
config.json
free of sensitive plaintext passwords.
View source details for citation from GitHub. Opens side panel.
The Bottom Line:
You do not need to build a bespoke authentication protocol for MCP.
If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.
View source details for citation from GitHub. Opens side panel.
Sources
Sources
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to:
Top Bar
Top Bar
Sidebar
Sidebar
Main Content
Main Content
Collapse sidebar Ctrl [
Collapse sidebar
Ctrl
[
Switch sites or apps
Switch sites or apps
Confluence
Search, press enter to navigate to advanced search with your text query
Create
Create
Rovo Ask Rovo
Ask Rovo
Notifications
Notifications
Help
Help
[EMAIL]
[EMAIL]
For you
For you
Recent
Recent
Starred
Starred
Spaces
Spaces
Apps
Apps
Product
Product
More actions
More actions
Back to top
Back to top
Content
Content
Create Create
Create
Change view
Change view
Search by title
Results will update as you type.
Product Strategy 2025+ Change emoji
Product Strategy 2025+
Product Strategy 2025+
Competitive analysis Change emoji
Competitive analysis
Competitive analysis
Ways of working - Product processes Change emoji
Ways of working - Product processes
Ways of working - Product processes
Core Product Performance Metrics 📈
Core Product Performance Metrics
📈
Core Product Performance Metrics
Product Documentation Change emoji
Product Documentation
Product Documentation
Product Briefs Change emoji
Product Briefs
Product Briefs
Change emoji Activity Export
Activity Export
Change emoji Autologging activities
Autologging activities
Deal Insights - Multiple currencies
Deal Insights - Multiple currencies
Reinvent Themes & Topics (and ACS)
Reinvent Themes & Topics (and ACS)
Change emoji Billing Portal
Billing Portal
Change emoji Upload Video/Audio Recordings
Upload Video/Audio Recordings
White-Label Jiminny Instance
White-Label Jiminny Instance
Win/Loss Analysis for a Deal
Win/Loss Analysis for a Deal
Change emoji Hubspot app
Hubspot app
Change emoji Automatically hard delete data for churned customers
Automatically hard delete data for churned customers
Change emoji Ask Jiminny Anything on Call level
Ask Jiminny Anything on Call level
Change emoji Ask Jiminny for Open and Closed Deals
Ask Jiminny for Open and Closed Deals
Change emoji Ask Jiminny Anything on Deal level
Ask Jiminny Anything on Deal level
Change emoji Automatically record all calendar meetings
Automatically record all calendar meetings
Change emoji Product Tiering
Product Tiering
Change emoji Recording Consent
Recording Consent
Change emoji Automated CRM Filling
Automated CRM Filling
Change emoji Automated Exec Reports
Automated Exec Reports
Change emoji Auto-detect Activity Type
Auto-detect Activity Type
AI Signals & Alerts
AI Signals & Alerts
Change emoji AJA on Anything
AJA on Anything
Change emoji AI Call Scoring
AI Call Scoring
Jiminny MCP Connector
Jiminny MCP Connector
Desktop app to record a meetings without visible Notetaker
Desktop app to record a meetings without visible Notetaker
Feedback Change emoji
Feedback
Feedback
Research & User Feedback Change emoji
Research & User Feedback
Research & User Feedback
Create
Create
Jira , (opens new window)
Jira
, (opens new window)
Teams , (opens new window)
Teams
, (opens new window)
open menu
open menu
More
More
Side Navigation Drag Handle
Breadcrumbs
Jiminny MCP Connector
Updated 10m ago
Updated 10m ago
Overflow menu
2
Edit this content
Edit
Share, Open - Anyone in the space can edit
Share
Copy link
Copy link
More actions
More actions
Jiminny MCP Connector
Jiminny MCP Connector
Jiminny MCP Connector
By Galya Dimitrova
By Galya Dimitrova
Read time 12 min
12 min
Views 14
14
Add a reaction
Add a reaction
Epic
Link to Epik in Jira
Document status
DRAFT
Epic
Document status
Link to Epik in Jira
DRAFT
Change emoji Objective
Objective
Change emoji Objective
Enable customers to connect Jiminny data to external AI tools (Claude, OpenAI, Gemini) so it can be used as part of their broader knowledge base and workflows.
Position Jiminny as a
data layer for AI-driven revenue workflows
, not just a standalone product.
👤 Target user
👤 Target user
👤 Target user
Revenue teams using AI tools (Sales,
CS...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.28307846,"top":0.0518755,"width":0.07596409,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"SevenShores\\Hubspot\\Exceptions\\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {\"status\":\"error\",\"message\":\"You have reached your secondly limit.\",\"errorType\":\"RATE_LIMIT","depth":4,"bounds":{"left":0.28125,"top":0.09497207,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SevenShores\\Hubspot\\Exceptions\\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {\"status\":\"error\",\"message\":\"You have reached your secondly limit.\",\"errorType\":\"RATE_LIMIT","depth":5,"bounds":{"left":0.2945479,"top":0.10614525,"width":0.4644282,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":4,"bounds":{"left":0.28125,"top":0.12769353,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":5,"bounds":{"left":0.2945479,"top":0.13886672,"width":0.11319814,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny MCP Connector - Product - Confluence","depth":4,"bounds":{"left":0.28125,"top":0.16041501,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny MCP Connector - Product - Confluence","depth":5,"bounds":{"left":0.2945479,"top":0.17158818,"width":0.08294548,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.28125,"top":0.19313647,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.2945479,"top":0.20430966,"width":0.013131649,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Workers | Datadog","depth":4,"bounds":{"left":0.28125,"top":0.22585794,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Workers | Datadog","depth":5,"bounds":{"left":0.2945479,"top":0.23703113,"width":0.032081116,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.28125,"top":0.2585794,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.2945479,"top":0.2697526,"width":0.04537899,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira","depth":4,"bounds":{"left":0.28125,"top":0.29130086,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira","depth":5,"bounds":{"left":0.2945479,"top":0.30247405,"width":0.15791224,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":4,"bounds":{"left":0.28125,"top":0.32402235,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":5,"bounds":{"left":0.2945479,"top":0.33519554,"width":0.16555852,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.28125,"top":0.3567438,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.2945479,"top":0.367917,"width":0.013131649,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"CloudWatch | us-east-2","depth":4,"bounds":{"left":0.28125,"top":0.38946527,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CloudWatch | us-east-2","depth":5,"bounds":{"left":0.2945479,"top":0.40063846,"width":0.041223403,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Your 'Not enpough activities' report wasn't generated - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.28125,"top":0.42218676,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your 'Not enpough activities' report wasn't generated - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.2945479,"top":0.43335995,"width":0.16821809,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny MCP Connector - Product - Confluence","depth":4,"bounds":{"left":0.28125,"top":0.45490822,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jiminny MCP Connector - Product - Confluence","depth":5,"bounds":{"left":0.2945479,"top":0.4660814,"width":0.08294548,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.34857047,"top":0.46209097,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.2840758,"top":0.48922586,"width":0.07413564,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.2840758,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.29504654,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.30618352,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.31732047,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.32845744,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.4659242,"top":0.055067837,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.47789228,"top":0.055067837,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.47523272,"top":0.103751,"width":0.013297873,"height":0.031923383},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.3648604,"top":0.103751,"width":0.013297873,"height":0.031923383},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.44730717,"top":0.103751,"width":0.013297873,"height":0.031923383},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.46060506,"top":0.103751,"width":0.013297873,"height":0.031923383},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.36053857,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.36053857,"top":0.15003991,"width":0.1200133,"height":0.025538707},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said what is the oauth required for mcp for clauide code and claude chat","depth":21,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"what is the oauth required for mcp for clauide code and claude chat","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP acts as a universal \"USB port\" that allows AI models to connect to external data sources and tools.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When connecting to remote, HTTP-based MCP servers, Anthropic relies on the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"OAuth 2.1","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"authorization framework.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"+2","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is exactly what is required and how OAuth is handled across both environments.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"The Underlying OAuth Flow for MCP","depth":23,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Underlying OAuth Flow for MCP","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Claude application acts as the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"OAuth Client","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and the remote tool you are connecting to acts as the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Resource Server","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:","depth":24,"bounds":{"left":0.3695146,"top":0.0,"width":0.11087101,"height":0.057861134},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Initial Handshake (Discovery):","depth":26,"bounds":{"left":0.38148272,"top":0.0,"width":0.07829122,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude attempts to connect to the MCP server without credentials. The server must reject this with a","depth":26,"bounds":{"left":0.38148272,"top":0.0,"width":0.1043883,"height":0.07861133},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"401 Unauthorized","depth":27,"bounds":{"left":0.40940824,"top":0.035115723,"width":0.044714097,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"HTTP status and include a","depth":26,"bounds":{"left":0.38148272,"top":0.033918597,"width":0.09042553,"height":0.037110932},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"WWW-Authenticate","depth":27,"bounds":{"left":0.43334442,"top":0.05586592,"width":0.044714097,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"header pointing to a Protected Resource Metadata (PRM) document.","depth":26,"bounds":{"left":0.38148272,"top":0.054668795,"width":0.09990027,"height":0.057861134},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Metadata Fetch:","depth":26,"bounds":{"left":0.38148272,"top":0.12330407,"width":0.042220745,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).","depth":26,"bounds":{"left":0.38148272,"top":0.12330407,"width":0.09840426,"height":0.07861133},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"bounds":{"left":0.45960772,"top":0.1859537,"width":0.008643617,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"User Consent:","depth":26,"bounds":{"left":0.38148272,"top":0.21268955,"width":0.036402926,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude prompts the user to authenticate.","depth":26,"bounds":{"left":0.38148272,"top":0.21268955,"width":0.1043883,"height":0.037110932},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., \"Allow Claude to access your GitHub repositories\").","depth":26,"bounds":{"left":0.38148272,"top":0.23343974,"width":0.1043883,"height":0.09936153},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citations from GitHub and Model Context Protocol. Opens side panel.","depth":26,"bounds":{"left":0.4175532,"top":0.31683958,"width":0.013796543,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"+1","depth":28,"bounds":{"left":0.4242021,"top":0.31883478,"width":0.0038231383,"height":0.012370312},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Token Exchange:","depth":26,"bounds":{"left":0.38148272,"top":0.34357542,"width":0.043716755,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.","depth":26,"bounds":{"left":0.38148272,"top":0.34357542,"width":0.099734046,"height":0.07861133},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Upstash. Opens side panel.","depth":26,"bounds":{"left":0.44198802,"top":0.40622506,"width":0.008643617,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Access Granted:","depth":26,"bounds":{"left":0.38148272,"top":0.4329609,"width":0.04255319,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.","depth":26,"bounds":{"left":0.38148272,"top":0.4329609,"width":0.1043883,"height":0.07861133},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"bounds":{"left":0.4119016,"top":0.49561054,"width":0.008643617,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"MCP OAuth in Claude Code","depth":23,"bounds":{"left":0.3695146,"top":0.5494813,"width":0.11635638,"height":0.01915403},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP OAuth in Claude Code","depth":24,"bounds":{"left":0.3695146,"top":0.5510774,"width":0.07047872,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:","depth":24,"bounds":{"left":0.3695146,"top":0.5774142,"width":0.11469415,"height":0.09936153},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browser Handoff:","depth":26,"bounds":{"left":0.38115028,"top":0.6875499,"width":0.04537899,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).","depth":26,"bounds":{"left":0.38115028,"top":0.6875499,"width":0.1043883,"height":0.12011173},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication:","depth":26,"bounds":{"left":0.38115028,"top":0.8184357,"width":0.040226065,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.","depth":26,"bounds":{"left":0.38115028,"top":0.8184357,"width":0.10472074,"height":0.12011173},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"bounds":{"left":0.39660904,"top":0.9225858,"width":0.008643617,"height":0.015961692},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Alternative (Static Headers):","depth":26,"bounds":{"left":0.38115028,"top":0.9493216,"width":0.07430186,"height":0.016360734},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.","depth":26,"bounds":{"left":0.38115028,"top":0.9493216,"width":0.10472074,"height":0.050678372},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You can do this using the","depth":26,"bounds":{"left":0.38115028,"top":1.0,"width":0.09690824,"height":-0.032322407},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"--header","depth":27,"bounds":{"left":0.44481382,"top":1.0,"width":0.022273935,"height":-0.05426979},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"flag (e.g.,","depth":26,"bounds":{"left":0.38115028,"top":1.0,"width":0.09990027,"height":-0.05307257},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"claude mcp add my-server --transport http --header \"Authorization: Bearer your-token\"","depth":27,"bounds":{"left":0.38115028,"top":1.0,"width":0.094913565,"height":-0.075019956},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from TrueFoundry. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"MCP OAuth in Claude Chat (Claude Desktop & Web)","depth":23,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP OAuth in Claude Chat (Claude Desktop & Web)","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\"Claude Chat\" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude.ai (Web Custom Connectors):","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When you add a remote MCP server via the \"Custom Connectors\" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Desktop:","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Desktop traditionally relies on a local configuration file (","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"claude_desktop_config.json","depth":27,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"config.json","depth":27,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"free of sensitive plaintext passwords.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from GitHub. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"The Bottom Line:","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You do not need to build a bespoke authentication protocol for MCP.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from GitHub. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Sources","depth":23,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sources","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextArea","text":"Enter a prompt for Gemini\nencrypted","depth":20,"bounds":{"left":0.37416887,"top":0.8104549,"width":0.10638298,"height":0.01915403},"value":"Enter a prompt for Gemini\nencrypted","help_text":"","role_description":"text entry area","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enter a prompt for Gemini","depth":21,"bounds":{"left":0.38081783,"top":0.81085396,"width":0.069980055,"height":0.018355945},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"encrypted","depth":21,"bounds":{"left":0.37317154,"top":0.8104549,"width":0.0066489363,"height":0.01915403},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open upload file menu","depth":20,"bounds":{"left":0.37017953,"top":0.8447725,"width":0.013297873,"height":0.031923383},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tools","depth":18,"bounds":{"left":0.38613698,"top":0.8447725,"width":0.013297873,"height":0.031923383},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open mode picker","depth":20,"bounds":{"left":0.4431516,"top":0.8439745,"width":0.026097074,"height":0.031923383},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pro","depth":23,"bounds":{"left":0.44847074,"top":0.8527534,"width":0.007480053,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Microphone","depth":19,"bounds":{"left":0.47124335,"top":0.8439745,"width":0.013297873,"height":0.031923383},"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.","depth":17,"bounds":{"left":0.3665226,"top":0.896249,"width":0.12167553,"height":0.025139665},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Your privacy & Gemini Opens in a new window","depth":17,"bounds":{"left":0.40724733,"top":0.92178774,"width":0.040226065,"height":0.012370312},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your privacy & Gemini","depth":18,"bounds":{"left":0.40724733,"top":0.92178774,"width":0.040226065,"height":0.012370312},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Opens in a new window","depth":19,"bounds":{"left":0.36053857,"top":0.92098963,"width":0.043218084,"height":0.012370312},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Summarize page","depth":7,"bounds":{"left":0.36619017,"top":0.95730245,"width":0.053523935,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Summarize page","depth":9,"bounds":{"left":0.37184176,"top":0.96249,"width":0.042220745,"height":0.015163607},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Skip to:","depth":10,"bounds":{"left":0.50731385,"top":0.07861133,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Top Bar","depth":11,"bounds":{"left":0.50731385,"top":0.097765364,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Top Bar","depth":12,"bounds":{"left":0.50731385,"top":0.097765364,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Sidebar","depth":11,"bounds":{"left":0.50731385,"top":0.11691939,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sidebar","depth":12,"bounds":{"left":0.50731385,"top":0.11691939,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Main Content","depth":11,"bounds":{"left":0.50731385,"top":0.13607343,"width":0.029421542,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Main Content","depth":12,"bounds":{"left":0.50731385,"top":0.13607343,"width":0.029421542,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Collapse sidebar Ctrl [","depth":10,"bounds":{"left":0.5006649,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Collapse sidebar","depth":12,"bounds":{"left":0.50581783,"top":0.066640064,"width":0.03673537,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Ctrl","depth":13,"bounds":{"left":0.5465425,"top":0.066640064,"width":0.007978723,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[","depth":13,"bounds":{"left":0.5611702,"top":0.066640064,"width":0.0016622341,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Switch sites or apps","depth":12,"bounds":{"left":0.51263297,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Switch sites or apps","depth":14,"bounds":{"left":0.5177859,"top":0.06344773,"width":0.044215426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Confluence","depth":10,"bounds":{"left":0.5259308,"top":0.057861134,"width":0.029421542,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXComboBox","text":"Search, press enter to navigate to advanced search with your text query","depth":11,"bounds":{"left":0.62400264,"top":0.06264964,"width":0.22140957,"height":0.015961692},"help_text":"","placeholder":"Search Confluence, Jira, Google Drive and other apps","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Create","depth":10,"bounds":{"left":0.8537234,"top":0.057861134,"width":0.030086435,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create","depth":12,"bounds":{"left":0.8650266,"top":0.06384677,"width":0.014793883,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Rovo Ask Rovo","depth":13,"bounds":{"left":0.92420214,"top":0.057861134,"width":0.036070477,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Rovo","depth":15,"bounds":{"left":0.93550533,"top":0.06384677,"width":0.020777926,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Notifications","depth":13,"bounds":{"left":0.9616024,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Notifications","depth":15,"bounds":{"left":0.96675533,"top":0.06344773,"width":0.027759308,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Help","depth":13,"bounds":{"left":0.97357047,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Help","depth":15,"bounds":{"left":0.9787234,"top":0.06344773,"width":0.010139627,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"lukas.kovalik@jiminny.com","depth":13,"bounds":{"left":0.98553854,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lukas.kovalik@jiminny.com","depth":15,"bounds":{"left":0.9906915,"top":0.06344773,"width":0.009308517,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"For you","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"For you","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Recent","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Recent","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Starred","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Starred","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Spaces","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Spaces","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Apps","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Apps","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product","depth":13,"bounds":{"left":0.5006649,"top":0.09976058,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product","depth":16,"bounds":{"left":0.5113032,"top":0.10574621,"width":0.017453458,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions","depth":14,"bounds":{"left":0.6452792,"top":0.10295291,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Back to top","depth":11,"bounds":{"left":0.5568484,"top":0.1396648,"width":0.04155585,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Back to top","depth":13,"bounds":{"left":0.5696476,"top":0.14644852,"width":0.021276595,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Content","depth":12,"bounds":{"left":0.5006649,"top":0.0,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Content","depth":15,"bounds":{"left":0.5113032,"top":0.0,"width":0.01761968,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Create Create","depth":14,"bounds":{"left":0.63730055,"top":0.0,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Create","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Change view","depth":13,"bounds":{"left":0.6452792,"top":0.0,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Change view","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Search by title","depth":15,"bounds":{"left":0.51163566,"top":0.005586592,"width":0.14261968,"height":0.022346368},"role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Results will update as you type.","depth":15,"bounds":{"left":0.50332445,"top":0.00518755,"width":0.06931516,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Strategy 2025+ Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.035913806,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Strategy 2025+","depth":17,"bounds":{"left":0.50598407,"top":0.039106146,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Product Strategy 2025+","depth":19,"bounds":{"left":0.52393615,"top":0.041899443,"width":0.053523935,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Competitive analysis Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.061452515,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Competitive analysis","depth":17,"bounds":{"left":0.50598407,"top":0.06464485,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Competitive analysis","depth":19,"bounds":{"left":0.52393615,"top":0.06743815,"width":0.046210106,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Ways of working - Product processes Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.08699122,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Ways of working - Product processes","depth":17,"bounds":{"left":0.50598407,"top":0.090183556,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Ways of working - Product processes","depth":19,"bounds":{"left":0.52393615,"top":0.09297685,"width":0.08361037,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Core Product Performance Metrics 📈","depth":16,"bounds":{"left":0.5046542,"top":0.112529926,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Core Product Performance Metrics","depth":17,"bounds":{"left":0.50598407,"top":0.11572227,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"📈","depth":18,"bounds":{"left":0.5159575,"top":0.118515566,"width":0.004654255,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Core Product Performance Metrics","depth":19,"bounds":{"left":0.52393615,"top":0.118515566,"width":0.0774601,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Documentation Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.13806863,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Documentation","depth":17,"bounds":{"left":0.50598407,"top":0.14126097,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Product Documentation","depth":19,"bounds":{"left":0.52393615,"top":0.14405426,"width":0.05269282,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Briefs Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.16360734,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Briefs","depth":17,"bounds":{"left":0.50598407,"top":0.16679968,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Product Briefs","depth":19,"bounds":{"left":0.52393615,"top":0.16959298,"width":0.03174867,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Activity Export","depth":18,"bounds":{"left":0.5099734,"top":0.18914606,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Activity Export","depth":21,"bounds":{"left":0.52925533,"top":0.19513169,"width":0.03274601,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Autologging activities","depth":18,"bounds":{"left":0.5099734,"top":0.21468475,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Autologging activities","depth":21,"bounds":{"left":0.52925533,"top":0.22067039,"width":0.048204787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Deal Insights - Multiple currencies","depth":18,"bounds":{"left":0.5099734,"top":0.24022347,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Deal Insights - Multiple currencies","depth":21,"bounds":{"left":0.52925533,"top":0.2462091,"width":0.076961435,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Reinvent Themes & Topics (and ACS)","depth":18,"bounds":{"left":0.5099734,"top":0.26576218,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Reinvent Themes & Topics (and ACS)","depth":21,"bounds":{"left":0.52925533,"top":0.2717478,"width":0.082446806,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Billing Portal","depth":18,"bounds":{"left":0.5099734,"top":0.29130086,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Billing Portal","depth":21,"bounds":{"left":0.52925533,"top":0.2972865,"width":0.027759308,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Upload Video/Audio Recordings","depth":18,"bounds":{"left":0.5099734,"top":0.31683958,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Upload Video/Audio Recordings","depth":21,"bounds":{"left":0.52925533,"top":0.32282522,"width":0.0709774,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"White-Label Jiminny Instance","depth":18,"bounds":{"left":0.5099734,"top":0.3423783,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"White-Label Jiminny Instance","depth":21,"bounds":{"left":0.52925533,"top":0.34836394,"width":0.066821806,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Win/Loss Analysis for a Deal","depth":18,"bounds":{"left":0.5099734,"top":0.367917,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Win/Loss Analysis for a Deal","depth":21,"bounds":{"left":0.52925533,"top":0.37390262,"width":0.06349734,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Hubspot app","depth":18,"bounds":{"left":0.5099734,"top":0.3934557,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Hubspot app","depth":21,"bounds":{"left":0.52925533,"top":0.39944133,"width":0.028590426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automatically hard delete data for churned customers","depth":18,"bounds":{"left":0.5099734,"top":0.41899443,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automatically hard delete data for churned customers","depth":21,"bounds":{"left":0.52925533,"top":0.42498004,"width":0.11968085,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny Anything on Call level","depth":18,"bounds":{"left":0.5099734,"top":0.4445331,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny Anything on Call level","depth":21,"bounds":{"left":0.52925533,"top":0.45051876,"width":0.076296546,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny for Open and Closed Deals","depth":18,"bounds":{"left":0.5099734,"top":0.47007182,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny for Open and Closed Deals","depth":21,"bounds":{"left":0.52925533,"top":0.47605747,"width":0.087765954,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny Anything on Deal level","depth":18,"bounds":{"left":0.5099734,"top":0.49561054,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny Anything on Deal level","depth":21,"bounds":{"left":0.52925533,"top":0.50159615,"width":0.077792555,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automatically record all calendar meetings","depth":18,"bounds":{"left":0.5099734,"top":0.5211492,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automatically record all calendar meetings","depth":21,"bounds":{"left":0.52925533,"top":0.5271349,"width":0.094913565,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Product Tiering","depth":18,"bounds":{"left":0.5099734,"top":0.54668796,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product Tiering","depth":21,"bounds":{"left":0.52925533,"top":0.5526736,"width":0.034408245,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Recording Consent","depth":18,"bounds":{"left":0.5099734,"top":0.57222664,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recording Consent","depth":21,"bounds":{"left":0.52925533,"top":0.57821226,"width":0.04255319,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automated CRM Filling","depth":18,"bounds":{"left":0.5099734,"top":0.5977654,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automated CRM Filling","depth":21,"bounds":{"left":0.52925533,"top":0.603751,"width":0.05069814,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automated Exec Reports","depth":18,"bounds":{"left":0.5099734,"top":0.62330407,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automated Exec Reports","depth":21,"bounds":{"left":0.52925533,"top":0.6292897,"width":0.054853722,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Auto-detect Activity Type","depth":18,"bounds":{"left":0.5099734,"top":0.64884275,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Auto-detect Activity Type","depth":21,"bounds":{"left":0.52925533,"top":0.6548284,"width":0.05718085,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"AI Signals & Alerts","depth":18,"bounds":{"left":0.5099734,"top":0.6743815,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI Signals & Alerts","depth":21,"bounds":{"left":0.52925533,"top":0.6803671,"width":0.04155585,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji AJA on Anything","depth":18,"bounds":{"left":0.5099734,"top":0.6999202,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AJA on Anything","depth":21,"bounds":{"left":0.52925533,"top":0.70590585,"width":0.03723404,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji AI Call Scoring","depth":18,"bounds":{"left":0.5099734,"top":0.7254589,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI Call Scoring","depth":21,"bounds":{"left":0.52925533,"top":0.73144454,"width":0.03357713,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jiminny MCP Connector","depth":18,"bounds":{"left":0.5099734,"top":0.7509976,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":21,"bounds":{"left":0.52925533,"top":0.7569832,"width":0.053856384,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop app to record a meetings without visible Notetaker","depth":18,"bounds":{"left":0.5099734,"top":0.7765363,"width":0.14461437,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop app to record a meetings without visible Notetaker","depth":21,"bounds":{"left":0.52925533,"top":0.78252196,"width":0.13164894,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Feedback Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.802075,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Feedback","depth":17,"bounds":{"left":0.50598407,"top":0.80526733,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Feedback","depth":19,"bounds":{"left":0.52393615,"top":0.80806065,"width":0.021775266,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Research & User Feedback Change emoji","depth":16,"bounds":{"left":0.5046542,"top":0.8276137,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Research & User Feedback","depth":17,"bounds":{"left":0.50598407,"top":0.8308061,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Research & User Feedback","depth":19,"bounds":{"left":0.52393615,"top":0.8335994,"width":0.059840426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create","depth":16,"bounds":{"left":0.5046542,"top":0.85315245,"width":0.14993352,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create","depth":19,"bounds":{"left":0.5152925,"top":0.8591381,"width":0.014793883,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jira , (opens new window)","depth":14,"bounds":{"left":0.5006649,"top":0.9042298,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jira","depth":18,"bounds":{"left":0.5113032,"top":0.9102155,"width":0.00831117,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", (opens new window)","depth":16,"bounds":{"left":0.5006649,"top":0.91779727,"width":0.04837101,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Teams , (opens new window)","depth":14,"bounds":{"left":0.5006649,"top":0.92976856,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Teams","depth":18,"bounds":{"left":0.5113032,"top":0.9357542,"width":0.014793883,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", (opens new window)","depth":16,"bounds":{"left":0.5006649,"top":0.943336,"width":0.04837101,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"open menu","depth":15,"bounds":{"left":0.64328456,"top":0.93296087,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"open menu","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More","depth":13,"bounds":{"left":0.5006649,"top":0.9648843,"width":0.15392287,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":16,"bounds":{"left":0.5113032,"top":0.9708699,"width":0.011469414,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Side Navigation Drag Handle","depth":14,"bounds":{"left":0.71043885,"top":0.0981644,"width":0.0631649,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Breadcrumbs","depth":15,"bounds":{"left":0.66289896,"top":0.09936153,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":17,"bounds":{"left":0.68417555,"top":0.105347164,"width":0.0546875,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Updated 10m ago","depth":16,"bounds":{"left":0.8374335,"top":0.105347164,"width":0.03873005,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Updated 10m ago","depth":17,"bounds":{"left":0.8374335,"top":0.105347164,"width":0.03873005,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Overflow menu","depth":18,"bounds":{"left":0.904754,"top":0.1009577,"width":0.00930851,"height":0.022346368},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2","depth":20,"bounds":{"left":0.90807843,"top":0.10614525,"width":0.002493351,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Edit this content","depth":16,"bounds":{"left":0.91738695,"top":0.09936153,"width":0.023603724,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Edit","depth":18,"bounds":{"left":0.92869014,"top":0.105347164,"width":0.00831117,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Share, Open - Anyone in the space can edit","depth":16,"bounds":{"left":0.94298536,"top":0.09936153,"width":0.02825798,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Share","depth":18,"bounds":{"left":0.95428854,"top":0.105347164,"width":0.012965426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy link","depth":16,"bounds":{"left":0.9709109,"top":0.09936153,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Copy link","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions","depth":15,"bounds":{"left":0.9835439,"top":0.09936153,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Jiminny MCP Connector","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Jiminny MCP Connector","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"By Galya Dimitrova","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"By Galya Dimitrova","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Read time 12 min","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"12 min","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Views 14","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"14","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Add a reaction","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Add a reaction","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Epic","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Link to Epik in Jira","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Document status","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"DRAFT","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Epic","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Document status","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Link to Epik in Jira","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"DRAFT","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Change emoji Objective","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Objective","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Change emoji Objective","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enable customers to connect Jiminny data to external AI tools (Claude, OpenAI, Gemini) so it can be used as part of their broader knowledge base and workflows.","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Position Jiminny as a","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"data layer for AI-driven revenue workflows","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", not just a standalone product.","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"👤 Target user","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"👤 Target user","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"👤 Target user","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Revenue teams using AI tools (Sales,","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"CS","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
6686704571581857270
|
7934688911725908726
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny
Jiminny
Workers | Datadog
Workers | Datadog
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
CloudWatch | us-east-2
CloudWatch | us-east-2
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
Edit
You said what is the oauth required for mcp for clauide code and claude chat
You said
what is the oauth required for mcp for clauide code and claude chat
Listen
Show more options
Show thinking
Gemini said
Gemini said
To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.
MCP acts as a universal "USB port" that allows AI models to connect to external data sources and tools.
When connecting to remote, HTTP-based MCP servers, Anthropic relies on the
OAuth 2.1
authorization framework.
This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.
View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.
+2
Here is exactly what is required and how OAuth is handled across both environments.
The Underlying OAuth Flow for MCP
The Underlying OAuth Flow for MCP
Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.
The Claude application acts as the
OAuth Client
, and the remote tool you are connecting to acts as the
Resource Server
.
View source details for citation from Model Context Protocol. Opens side panel.
To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:
Initial Handshake (Discovery):
Claude attempts to connect to the MCP server without credentials. The server must reject this with a
401 Unauthorized
HTTP status and include a
WWW-Authenticate
header pointing to a Protected Resource Metadata (PRM) document.
Metadata Fetch:
Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).
View source details for citation from Model Context Protocol. Opens side panel.
User Consent:
Claude prompts the user to authenticate.
The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., "Allow Claude to access your GitHub repositories").
View source details for citations from GitHub and Model Context Protocol. Opens side panel.
+1
Token Exchange:
Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.
View source details for citation from Upstash. Opens side panel.
Access Granted:
Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.
View source details for citation from Model Context Protocol. Opens side panel.
MCP OAuth in Claude Code
MCP OAuth in Claude Code
Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:
Browser Handoff:
When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).
Authentication:
Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.
View source details for citation from Model Context Protocol. Opens side panel.
Alternative (Static Headers):
If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.
You can do this using the
--header
flag (e.g.,
claude mcp add my-server --transport http --header "Authorization: Bearer your-token"
).
View source details for citation from TrueFoundry. Opens side panel.
MCP OAuth in Claude Chat (Claude Desktop & Web)
MCP OAuth in Claude Chat (Claude Desktop & Web)
"Claude Chat" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.
Claude.ai (Web Custom Connectors):
When you add a remote MCP server via the "Custom Connectors" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.
Claude Desktop:
Claude Desktop traditionally relies on a local configuration file (
claude_desktop_config.json
). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.
When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.
It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your
config.json
free of sensitive plaintext passwords.
View source details for citation from GitHub. Opens side panel.
The Bottom Line:
You do not need to build a bespoke authentication protocol for MCP.
If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.
View source details for citation from GitHub. Opens side panel.
Sources
Sources
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to:
Top Bar
Top Bar
Sidebar
Sidebar
Main Content
Main Content
Collapse sidebar Ctrl [
Collapse sidebar
Ctrl
[
Switch sites or apps
Switch sites or apps
Confluence
Search, press enter to navigate to advanced search with your text query
Create
Create
Rovo Ask Rovo
Ask Rovo
Notifications
Notifications
Help
Help
[EMAIL]
[EMAIL]
For you
For you
Recent
Recent
Starred
Starred
Spaces
Spaces
Apps
Apps
Product
Product
More actions
More actions
Back to top
Back to top
Content
Content
Create Create
Create
Change view
Change view
Search by title
Results will update as you type.
Product Strategy 2025+ Change emoji
Product Strategy 2025+
Product Strategy 2025+
Competitive analysis Change emoji
Competitive analysis
Competitive analysis
Ways of working - Product processes Change emoji
Ways of working - Product processes
Ways of working - Product processes
Core Product Performance Metrics 📈
Core Product Performance Metrics
📈
Core Product Performance Metrics
Product Documentation Change emoji
Product Documentation
Product Documentation
Product Briefs Change emoji
Product Briefs
Product Briefs
Change emoji Activity Export
Activity Export
Change emoji Autologging activities
Autologging activities
Deal Insights - Multiple currencies
Deal Insights - Multiple currencies
Reinvent Themes & Topics (and ACS)
Reinvent Themes & Topics (and ACS)
Change emoji Billing Portal
Billing Portal
Change emoji Upload Video/Audio Recordings
Upload Video/Audio Recordings
White-Label Jiminny Instance
White-Label Jiminny Instance
Win/Loss Analysis for a Deal
Win/Loss Analysis for a Deal
Change emoji Hubspot app
Hubspot app
Change emoji Automatically hard delete data for churned customers
Automatically hard delete data for churned customers
Change emoji Ask Jiminny Anything on Call level
Ask Jiminny Anything on Call level
Change emoji Ask Jiminny for Open and Closed Deals
Ask Jiminny for Open and Closed Deals
Change emoji Ask Jiminny Anything on Deal level
Ask Jiminny Anything on Deal level
Change emoji Automatically record all calendar meetings
Automatically record all calendar meetings
Change emoji Product Tiering
Product Tiering
Change emoji Recording Consent
Recording Consent
Change emoji Automated CRM Filling
Automated CRM Filling
Change emoji Automated Exec Reports
Automated Exec Reports
Change emoji Auto-detect Activity Type
Auto-detect Activity Type
AI Signals & Alerts
AI Signals & Alerts
Change emoji AJA on Anything
AJA on Anything
Change emoji AI Call Scoring
AI Call Scoring
Jiminny MCP Connector
Jiminny MCP Connector
Desktop app to record a meetings without visible Notetaker
Desktop app to record a meetings without visible Notetaker
Feedback Change emoji
Feedback
Feedback
Research & User Feedback Change emoji
Research & User Feedback
Research & User Feedback
Create
Create
Jira , (opens new window)
Jira
, (opens new window)
Teams , (opens new window)
Teams
, (opens new window)
open menu
open menu
More
More
Side Navigation Drag Handle
Breadcrumbs
Jiminny MCP Connector
Updated 10m ago
Updated 10m ago
Overflow menu
2
Edit this content
Edit
Share, Open - Anyone in the space can edit
Share
Copy link
Copy link
More actions
More actions
Jiminny MCP Connector
Jiminny MCP Connector
Jiminny MCP Connector
By Galya Dimitrova
By Galya Dimitrova
Read time 12 min
12 min
Views 14
14
Add a reaction
Add a reaction
Epic
Link to Epik in Jira
Document status
DRAFT
Epic
Document status
Link to Epik in Jira
DRAFT
Change emoji Objective
Objective
Change emoji Objective
Enable customers to connect Jiminny data to external AI tools (Claude, OpenAI, Gemini) so it can be used as part of their broader knowledge base and workflows.
Position Jiminny as a
data layer for AI-driven revenue workflows
, not just a standalone product.
👤 Target user
👤 Target user
👤 Target user
Revenue teams using AI tools (Sales,
CS...
|
71971
|
|
71960
|
1738
|
5
|
2026-04-22T14:34:25.204969+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776868465204_m1.jpg...
|
Firefox
|
Jiminny MCP Connector - Product - Confluence — Wor Jiminny MCP Connector - Product - Confluence — Work...
|
True
|
jiminny.atlassian.net/wiki/spaces/PROD/pages/37282 jiminny.atlassian.net/wiki/spaces/PROD/pages/3728244737/Jiminny+MCP+Connector...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny
Jiminny
Workers | Datadog
Workers | Datadog
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
CloudWatch | us-east-2
CloudWatch | us-east-2
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
Edit
You said what is the oauth required for mcp for clauide code and claude chat
You said
what is the oauth required for mcp for clauide code and claude chat
Listen
Show more options
Show thinking
Gemini said
Gemini said
To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.
MCP acts as a universal "USB port" that allows AI models to connect to external data sources and tools.
When connecting to remote, HTTP-based MCP servers, Anthropic relies on the
OAuth 2.1
authorization framework.
This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.
View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.
+2
Here is exactly what is required and how OAuth is handled across both environments.
The Underlying OAuth Flow for MCP
The Underlying OAuth Flow for MCP
Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.
The Claude application acts as the
OAuth Client
, and the remote tool you are connecting to acts as the
Resource Server
.
View source details for citation from Model Context Protocol. Opens side panel.
To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:
Initial Handshake (Discovery):
Claude attempts to connect to the MCP server without credentials. The server must reject this with a
401 Unauthorized
HTTP status and include a
WWW-Authenticate
header pointing to a Protected Resource Metadata (PRM) document.
Metadata Fetch:
Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).
View source details for citation from Model Context Protocol. Opens side panel.
User Consent:
Claude prompts the user to authenticate.
The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., "Allow Claude to access your GitHub repositories").
View source details for citations from GitHub and Model Context Protocol. Opens side panel.
+1
Token Exchange:
Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.
View source details for citation from Upstash. Opens side panel.
Access Granted:
Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.
View source details for citation from Model Context Protocol. Opens side panel.
MCP OAuth in Claude Code
MCP OAuth in Claude Code
Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:
Browser Handoff:
When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).
Authentication:
Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.
View source details for citation from Model Context Protocol. Opens side panel.
Alternative (Static Headers):
If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.
You can do this using the
--header
flag (e.g.,
claude mcp add my-server --transport http --header "Authorization: Bearer your-token"
).
View source details for citation from TrueFoundry. Opens side panel.
MCP OAuth in Claude Chat (Claude Desktop & Web)
MCP OAuth in Claude Chat (Claude Desktop & Web)
"Claude Chat" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.
Claude.ai (Web Custom Connectors):
When you add a remote MCP server via the "Custom Connectors" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.
Claude Desktop:
Claude Desktop traditionally relies on a local configuration file (
claude_desktop_config.json
). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.
When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.
It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your
config.json
free of sensitive plaintext passwords.
View source details for citation from GitHub. Opens side panel.
The Bottom Line:
You do not need to build a bespoke authentication protocol for MCP.
If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.
View source details for citation from GitHub. Opens side panel.
Sources
Sources
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to:
Top Bar
Top Bar
Sidebar
Sidebar
Main Content
Main Content
Collapse sidebar Ctrl [
Collapse sidebar
Ctrl
[
Switch sites or apps
Switch sites or apps
Confluence
Search, press enter to navigate to advanced search with your text query
Create
Create
Rovo Ask Rovo
Ask Rovo
Notifications
Notifications
Help
Help
[EMAIL]
[EMAIL]
For you
For you
Recent
Recent
Starred
Starred
Spaces
Spaces
Apps
Apps
Product
Product
More actions
More actions
Back to top
Back to top
Content
Content
Create Create
Create
Change view
Change view
Search by title
Results will update as you type.
Product Strategy 2025+ Change emoji
Product Strategy 2025+
Product Strategy 2025+
Competitive analysis Change emoji
Competitive analysis
Competitive analysis
Ways of working - Product processes Change emoji
Ways of working - Product processes
Ways of working - Product processes
Core Product Performance Metrics 📈
Core Product Performance Metrics
📈
Core Product Performance Metrics
Product Documentation Change emoji
Product Documentation
Product Documentation
Product Briefs Change emoji
Product Briefs
Product Briefs
Change emoji Activity Export
Activity Export
Change emoji Autologging activities
Autologging activities
Deal Insights - Multiple currencies
Deal Insights - Multiple currencies
Reinvent Themes & Topics (and ACS)
Reinvent Themes & Topics (and ACS)
Change emoji Billing Portal
Billing Portal
Change emoji Upload Video/Audio Recordings
Upload Video/Audio Recordings
White-Label Jiminny Instance
White-Label Jiminny Instance
Win/Loss Analysis for a Deal
Win/Loss Analysis for a Deal
Change emoji Hubspot app
Hubspot app
Change emoji Automatically hard delete data for churned customers
Automatically hard delete data for churned customers
Change emoji Ask Jiminny Anything on Call level
Ask Jiminny Anything on Call level
Change emoji Ask Jiminny for Open and Closed Deals
Ask Jiminny for Open and Closed Deals
Change emoji Ask Jiminny Anything on Deal level
Ask Jiminny Anything on Deal level
Change emoji Automatically record all calendar meetings
Automatically record all calendar meetings
Change emoji Product Tiering
Product Tiering
Change emoji Recording Consent
Recording Consent
Change emoji Automated CRM Filling
Automated CRM Filling
Change emoji Automated Exec Reports
Automated Exec Reports
Change emoji Auto-detect Activity Type
Auto-detect Activity Type
AI Signals & Alerts
AI Signals & Alerts
Change emoji AJA on Anything
AJA on Anything
Change emoji AI Call Scoring
AI Call Scoring
Jiminny MCP Connector
Jiminny MCP Connector
Desktop app to record a meetings without visible Notetaker
Desktop app to record a meetings without visible Notetaker
Feedback Change emoji
Feedback
Feedback
Research & User Feedback Change emoji
Research & User Feedback
Research & User Feedback
Create
Create
Jira , (opens new window)
Jira
, (opens new window)
Teams , (opens new window)
Teams
, (opens new window)
open menu
open menu
More
More
Side Navigation Drag Handle
Breadcrumbs
Jiminny MCP Connector
Updated 10m ago
Updated 10m ago
Edit this content
Edit
Share, Open - Anyone in the space can edit
Share
Copy link
Copy link
More actions
More actions
Jiminny MCP Connector
Jiminny MCP Connector
Jiminny MCP Connector
By Galya Dimitrova
By Galya Dimitrova
Read time 12 min
12 min
Views 14
14
Add a reaction
Add a reaction
Epic
Link to Epik in Jira
Document status
DRAFT
Epic
Document status
Link to Epik in Jira
DRAFT
Change emoji Objective
Objective
Change emoji Objective
Enable customers to connect Jiminny data to external AI tools (Claude, OpenAI, Gemini) so it can be used as part of their broader knowledge base and workflows.
Position Jiminny as a
data layer for AI-driven revenue workflows
, not just a standalone product.
👤 Target user...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"SevenShores\\Hubspot\\Exceptions\\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {\"status\":\"error\",\"message\":\"You have reached your secondly limit.\",\"errorType\":\"RATE_LIMIT","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SevenShores\\Hubspot\\Exceptions\\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {\"status\":\"error\",\"message\":\"You have reached your secondly limit.\",\"errorType\":\"RATE_LIMIT","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny MCP Connector - Product - Confluence","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny MCP Connector - Product - Confluence","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Workers | Datadog","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Workers | Datadog","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"CloudWatch | us-east-2","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CloudWatch | us-east-2","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Your 'Not enpough activities' report wasn't generated - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your 'Not enpough activities' report wasn't generated - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny MCP Connector - Product - Confluence","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jiminny MCP Connector - Product - Confluence","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.028819444,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.051736113,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.075,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.09826389,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.121527776,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Chat","depth":12,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said what is the oauth required for mcp for clauide code and claude chat","depth":21,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"what is the oauth required for mcp for clauide code and claude chat","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP acts as a universal \"USB port\" that allows AI models to connect to external data sources and tools.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When connecting to remote, HTTP-based MCP servers, Anthropic relies on the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"OAuth 2.1","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"authorization framework.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"+2","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is exactly what is required and how OAuth is handled across both environments.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"The Underlying OAuth Flow for MCP","depth":23,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Underlying OAuth Flow for MCP","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Claude application acts as the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"OAuth Client","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and the remote tool you are connecting to acts as the","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Resource Server","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Initial Handshake (Discovery):","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude attempts to connect to the MCP server without credentials. The server must reject this with a","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"401 Unauthorized","depth":27,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"HTTP status and include a","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"WWW-Authenticate","depth":27,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"header pointing to a Protected Resource Metadata (PRM) document.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Metadata Fetch:","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"User Consent:","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude prompts the user to authenticate.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., \"Allow Claude to access your GitHub repositories\").","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citations from GitHub and Model Context Protocol. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"+1","depth":28,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Token Exchange:","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Upstash. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Access Granted:","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"MCP OAuth in Claude Code","depth":23,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP OAuth in Claude Code","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Browser Handoff:","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication:","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from Model Context Protocol. Opens side panel.","depth":26,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Alternative (Static Headers):","depth":26,"bounds":{"left":0.23159721,"top":0.0,"width":0.15520833,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.","depth":26,"bounds":{"left":0.23159721,"top":0.0,"width":0.21875,"height":0.13833334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You can do this using the","depth":26,"bounds":{"left":0.23159721,"top":0.045,"width":0.20243056,"height":0.051666666},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"--header","depth":27,"bounds":{"left":0.36458334,"top":0.075555556,"width":0.046527777,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"flag (e.g.,","depth":26,"bounds":{"left":0.23159721,"top":0.07388889,"width":0.20868056,"height":0.051666666},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"claude mcp add my-server --transport http --header \"Authorization: Bearer your-token\"","depth":27,"bounds":{"left":0.23159721,"top":0.104444444,"width":0.19826388,"height":0.07833333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":26,"bounds":{"left":0.4340278,"top":0.16055556,"width":0.0069444445,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from TrueFoundry. Opens side panel.","depth":26,"bounds":{"left":0.23576389,"top":0.19,"width":0.018055556,"height":0.022222223},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"MCP OAuth in Claude Chat (Claude Desktop & Web)","depth":23,"bounds":{"left":0.20729166,"top":0.265,"width":0.24305555,"height":0.053333335},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MCP OAuth in Claude Chat (Claude Desktop & Web)","depth":24,"bounds":{"left":0.20729166,"top":0.26722223,"width":0.24131945,"height":0.049444444},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\"Claude Chat\" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.","depth":24,"bounds":{"left":0.20729166,"top":0.33055556,"width":0.21944444,"height":0.08055556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude.ai (Web Custom Connectors):","depth":26,"bounds":{"left":0.23159721,"top":0.4261111,"width":0.19930555,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When you add a remote MCP server via the \"Custom Connectors\" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.","depth":26,"bounds":{"left":0.23159721,"top":0.4261111,"width":0.21875,"height":0.2827778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Desktop:","depth":26,"bounds":{"left":0.23159721,"top":0.7238889,"width":0.08923611,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Claude Desktop traditionally relies on a local configuration file (","depth":26,"bounds":{"left":0.23159721,"top":0.7238889,"width":0.21284722,"height":0.08055556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"claude_desktop_config.json","depth":27,"bounds":{"left":0.2579861,"top":0.78333336,"width":0.15138888,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.","depth":26,"bounds":{"left":0.23159721,"top":0.7816667,"width":0.21736111,"height":0.13833334},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.","depth":26,"bounds":{"left":0.23159721,"top":0.8972222,"width":0.21076389,"height":0.10277778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your","depth":26,"bounds":{"left":0.23159721,"top":0.98388886,"width":0.21527778,"height":0.016111135},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"config.json","depth":27,"bounds":{"left":0.353125,"top":1.0,"width":0.06423611,"height":-0.07222223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"free of sensitive plaintext passwords.","depth":26,"bounds":{"left":0.23159721,"top":1.0,"width":0.21597221,"height":-0.07055557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from GitHub. Opens side panel.","depth":26,"bounds":{"left":0.39895833,"top":1.0,"width":0.018055556,"height":-0.100000024},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"The Bottom Line:","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You do not need to build a bespoke authentication protocol for MCP.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"View source details for citation from GitHub. Opens side panel.","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Sources","depth":23,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sources","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextArea","text":"Enter a prompt for Gemini\nencrypted","depth":20,"value":"Enter a prompt for Gemini\nencrypted","help_text":"","role_description":"text entry area","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enter a prompt for Gemini","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"encrypted","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open upload file menu","depth":20,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tools","depth":18,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open mode picker","depth":20,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pro","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Microphone","depth":19,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Your privacy & Gemini Opens in a new window","depth":17,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your privacy & Gemini","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Opens in a new window","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Summarize page","depth":7,"bounds":{"left":0.20034721,"top":0.0,"width":0.11180556,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Summarize page","depth":9,"bounds":{"left":0.21215278,"top":0.0,"width":0.088194445,"height":0.02111111},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Skip to:","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Top Bar","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Top Bar","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Sidebar","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sidebar","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Main Content","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Main Content","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Collapse sidebar Ctrl [","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Collapse sidebar","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Ctrl","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Switch sites or apps","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Switch sites or apps","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Confluence","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXComboBox","text":"Search, press enter to navigate to advanced search with your text query","depth":11,"help_text":"","placeholder":"Search Confluence, Jira, Google Drive and other apps","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Create","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Rovo Ask Rovo","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Rovo","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Notifications","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Notifications","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Help","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Help","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"lukas.kovalik@jiminny.com","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lukas.kovalik@jiminny.com","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"For you","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"For you","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Recent","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Recent","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Starred","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Starred","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Spaces","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Spaces","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Apps","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Apps","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions","depth":14,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Back to top","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Back to top","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Content","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Content","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Create Create","depth":14,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Create","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Change view","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Change view","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Search by title","depth":15,"role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Results will update as you type.","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Strategy 2025+ Change emoji","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Strategy 2025+","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Product Strategy 2025+","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Competitive analysis Change emoji","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Competitive analysis","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Competitive analysis","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Ways of working - Product processes Change emoji","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Ways of working - Product processes","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Ways of working - Product processes","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Core Product Performance Metrics 📈","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Core Product Performance Metrics","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"📈","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Core Product Performance Metrics","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Documentation Change emoji","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Documentation","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Product Documentation","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Product Briefs Change emoji","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Product Briefs","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Product Briefs","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Activity Export","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Activity Export","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Autologging activities","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Autologging activities","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Deal Insights - Multiple currencies","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Deal Insights - Multiple currencies","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Reinvent Themes & Topics (and ACS)","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Reinvent Themes & Topics (and ACS)","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Billing Portal","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Billing Portal","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Upload Video/Audio Recordings","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Upload Video/Audio Recordings","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"White-Label Jiminny Instance","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"White-Label Jiminny Instance","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Win/Loss Analysis for a Deal","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Win/Loss Analysis for a Deal","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Hubspot app","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Hubspot app","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automatically hard delete data for churned customers","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automatically hard delete data for churned customers","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny Anything on Call level","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny Anything on Call level","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny for Open and Closed Deals","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny for Open and Closed Deals","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Ask Jiminny Anything on Deal level","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny Anything on Deal level","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automatically record all calendar meetings","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automatically record all calendar meetings","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Product Tiering","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product Tiering","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Recording Consent","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Recording Consent","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automated CRM Filling","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automated CRM Filling","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Automated Exec Reports","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automated Exec Reports","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji Auto-detect Activity Type","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Auto-detect Activity Type","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"AI Signals & Alerts","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI Signals & Alerts","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji AJA on Anything","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AJA on Anything","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change emoji AI Call Scoring","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI Call Scoring","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jiminny MCP Connector","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Desktop app to record a meetings without visible Notetaker","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Desktop app to record a meetings without visible Notetaker","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Feedback Change emoji","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Feedback","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Feedback","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Research & User Feedback Change emoji","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Research & User Feedback","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Research & User Feedback","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jira , (opens new window)","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jira","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", (opens new window)","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Teams , (opens new window)","depth":14,"bounds":{"left":0.48125,"top":0.0,"width":0.32152778,"height":0.035555556},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Teams","depth":18,"bounds":{"left":0.5034722,"top":0.0,"width":0.030902777,"height":0.019444445},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", (opens new window)","depth":16,"bounds":{"left":0.48125,"top":0.0,"width":0.10104167,"height":0.019444445},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"open menu","depth":15,"bounds":{"left":0.77916664,"top":0.0,"width":0.008333334,"height":0.026666667},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"open menu","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More","depth":13,"bounds":{"left":0.48125,"top":0.0,"width":0.32152778,"height":0.035555556},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":16,"bounds":{"left":0.5034722,"top":0.0,"width":0.023958333,"height":0.019444445},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Side Navigation Drag Handle","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Breadcrumbs","depth":15,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Updated 10m ago","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Updated 10m ago","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Edit this content","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Edit","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Share, Open - Anyone in the space can edit","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Share","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy link","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Copy link","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions","depth":15,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Jiminny MCP Connector","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny MCP Connector","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Jiminny MCP Connector","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"By Galya Dimitrova","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"By Galya Dimitrova","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Read time 12 min","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"12 min","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Views 14","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"14","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Add a reaction","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Add a reaction","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Epic","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Link to Epik in Jira","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Document status","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"DRAFT","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Epic","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Document status","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Link to Epik in Jira","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"DRAFT","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Change emoji Objective","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Objective","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Change emoji Objective","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enable customers to connect Jiminny data to external AI tools (Claude, OpenAI, Gemini) so it can be used as part of their broader knowledge base and workflows.","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Position Jiminny as a","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"data layer for AI-driven revenue workflows","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", not just a standalone product.","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"👤 Target user","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"}]...
|
3290771524577391835
|
7934688909578425078
|
idle
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
SevenShores\Hubspot\Exceptions\BadRequest: Client error: `POST https://api.hubapi.com/crm/v3/objects/contact/search` resulted in a `429 Too Many Requests` response: {"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny
Jiminny
Workers | Datadog
Workers | Datadog
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
[JY-20728] [HubSpot] Find the root cause of 429 hit and tweak API client rate limiter - Jira
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
CloudWatch | us-east-2
CloudWatch | us-east-2
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Your 'Not enpough activities' report wasn't generated - [EMAIL] - Jiminny Mail
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
Edit
You said what is the oauth required for mcp for clauide code and claude chat
You said
what is the oauth required for mcp for clauide code and claude chat
Listen
Show more options
Show thinking
Gemini said
Gemini said
To understand the OAuth requirements for the Model Context Protocol (MCP) in Claude Code and Claude Chat, it helps to look at how MCP handles secure connections.
MCP acts as a universal "USB port" that allows AI models to connect to external data sources and tools.
When connecting to remote, HTTP-based MCP servers, Anthropic relies on the
OAuth 2.1
authorization framework.
This ensures that Claude can access your protected tools securely using temporary tokens, rather than requiring you to copy and paste long-lived API keys, passwords, or configuration secrets.
View source details for citations from Stytch, Model Context Protocol, and Zuplo. Opens side panel.
+2
Here is exactly what is required and how OAuth is handled across both environments.
The Underlying OAuth Flow for MCP
The Underlying OAuth Flow for MCP
Whether you are using Claude Code or Claude Chat, the underlying architecture is identical.
The Claude application acts as the
OAuth Client
, and the remote tool you are connecting to acts as the
Resource Server
.
View source details for citation from Model Context Protocol. Opens side panel.
To build or connect to an MCP server, the OAuth integration must support this standard discovery and token exchange flow:
Initial Handshake (Discovery):
Claude attempts to connect to the MCP server without credentials. The server must reject this with a
401 Unauthorized
HTTP status and include a
WWW-Authenticate
header pointing to a Protected Resource Metadata (PRM) document.
Metadata Fetch:
Claude reads the PRM document to locate your specific Authorization Server (e.g., Okta, Auth0, Clerk, or a custom auth server).
View source details for citation from Model Context Protocol. Opens side panel.
User Consent:
Claude prompts the user to authenticate.
The user is temporarily redirected to a browser window to log in and explicitly grant permission (e.g., "Allow Claude to access your GitHub repositories").
View source details for citations from GitHub and Model Context Protocol. Opens side panel.
+1
Token Exchange:
Claude receives a temporary authorization code via a redirect, which it silently exchanges for a long-lived access token.
View source details for citation from Upstash. Opens side panel.
Access Granted:
Claude now includes this token in the header of subsequent requests to invoke tools and read data on your behalf.
View source details for citation from Model Context Protocol. Opens side panel.
MCP OAuth in Claude Code
MCP OAuth in Claude Code
Claude Code is Anthropic's command-line interface (CLI) tool for software engineering. When you connect Claude Code to a protected remote MCP server, the OAuth flow is adapted for the terminal:
Browser Handoff:
When Claude Code hits an MCP server that requires OAuth, it will generate a secure authorization link and automatically open your default desktop web browser (or output the URL to the terminal for headless environments).
Authentication:
Once you log in and approve access in the browser, the authorization server redirects the payload back to a local loopback port managed by Claude Code, handing off the authorization code.
View source details for citation from Model Context Protocol. Opens side panel.
Alternative (Static Headers):
If setting up a full OAuth 2.1 server is too complex for a quick internal tool, Claude Code also allows you to bypass the browser flow by injecting static bearer tokens directly via the CLI.
You can do this using the
--header
flag (e.g.,
claude mcp add my-server --transport http --header "Authorization: Bearer your-token"
).
View source details for citation from TrueFoundry. Opens side panel.
MCP OAuth in Claude Chat (Claude Desktop & Web)
MCP OAuth in Claude Chat (Claude Desktop & Web)
"Claude Chat" encompasses the visual interfaces—both the Claude Desktop application and the Claude.ai web platform.
Claude.ai (Web Custom Connectors):
When you add a remote MCP server via the "Custom Connectors" menu in your settings, Claude.ai handles the OAuth flow entirely in the browser. You simply paste the MCP server URL. Claude automatically parses the 401 discovery response and redirects you to the third-party service to grant permission, before seamlessly dropping you back into your chat session.
Claude Desktop:
Claude Desktop traditionally relies on a local configuration file (
claude_desktop_config.json
). While local stdio servers often require pasting API keys into this file, remote HTTP MCP servers utilizing OAuth eliminate this security risk.
When Claude Desktop encounters an OAuth-protected server, it pops open your system's default browser to capture your consent.
It then stores the resulting token securely in your operating system's native keychain or credential manager, keeping your
config.json
free of sensitive plaintext passwords.
View source details for citation from GitHub. Opens side panel.
The Bottom Line:
You do not need to build a bespoke authentication protocol for MCP.
If your external tool already utilizes standard OpenID Connect or OAuth 2.0/2.1, Claude Code and Claude Chat are heavily standardized to automatically discover those endpoints, route the user for consent, and manage the resulting tokens behind the scenes.
View source details for citation from GitHub. Opens side panel.
Sources
Sources
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Your Jiminny chats aren’t used to improve our models. Gemini is AI and can make mistakes, including about people.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to:
Top Bar
Top Bar
Sidebar
Sidebar
Main Content
Main Content
Collapse sidebar Ctrl [
Collapse sidebar
Ctrl
[
Switch sites or apps
Switch sites or apps
Confluence
Search, press enter to navigate to advanced search with your text query
Create
Create
Rovo Ask Rovo
Ask Rovo
Notifications
Notifications
Help
Help
[EMAIL]
[EMAIL]
For you
For you
Recent
Recent
Starred
Starred
Spaces
Spaces
Apps
Apps
Product
Product
More actions
More actions
Back to top
Back to top
Content
Content
Create Create
Create
Change view
Change view
Search by title
Results will update as you type.
Product Strategy 2025+ Change emoji
Product Strategy 2025+
Product Strategy 2025+
Competitive analysis Change emoji
Competitive analysis
Competitive analysis
Ways of working - Product processes Change emoji
Ways of working - Product processes
Ways of working - Product processes
Core Product Performance Metrics 📈
Core Product Performance Metrics
📈
Core Product Performance Metrics
Product Documentation Change emoji
Product Documentation
Product Documentation
Product Briefs Change emoji
Product Briefs
Product Briefs
Change emoji Activity Export
Activity Export
Change emoji Autologging activities
Autologging activities
Deal Insights - Multiple currencies
Deal Insights - Multiple currencies
Reinvent Themes & Topics (and ACS)
Reinvent Themes & Topics (and ACS)
Change emoji Billing Portal
Billing Portal
Change emoji Upload Video/Audio Recordings
Upload Video/Audio Recordings
White-Label Jiminny Instance
White-Label Jiminny Instance
Win/Loss Analysis for a Deal
Win/Loss Analysis for a Deal
Change emoji Hubspot app
Hubspot app
Change emoji Automatically hard delete data for churned customers
Automatically hard delete data for churned customers
Change emoji Ask Jiminny Anything on Call level
Ask Jiminny Anything on Call level
Change emoji Ask Jiminny for Open and Closed Deals
Ask Jiminny for Open and Closed Deals
Change emoji Ask Jiminny Anything on Deal level
Ask Jiminny Anything on Deal level
Change emoji Automatically record all calendar meetings
Automatically record all calendar meetings
Change emoji Product Tiering
Product Tiering
Change emoji Recording Consent
Recording Consent
Change emoji Automated CRM Filling
Automated CRM Filling
Change emoji Automated Exec Reports
Automated Exec Reports
Change emoji Auto-detect Activity Type
Auto-detect Activity Type
AI Signals & Alerts
AI Signals & Alerts
Change emoji AJA on Anything
AJA on Anything
Change emoji AI Call Scoring
AI Call Scoring
Jiminny MCP Connector
Jiminny MCP Connector
Desktop app to record a meetings without visible Notetaker
Desktop app to record a meetings without visible Notetaker
Feedback Change emoji
Feedback
Feedback
Research & User Feedback Change emoji
Research & User Feedback
Research & User Feedback
Create
Create
Jira , (opens new window)
Jira
, (opens new window)
Teams , (opens new window)
Teams
, (opens new window)
open menu
open menu
More
More
Side Navigation Drag Handle
Breadcrumbs
Jiminny MCP Connector
Updated 10m ago
Updated 10m ago
Edit this content
Edit
Share, Open - Anyone in the space can edit
Share
Copy link
Copy link
More actions
More actions
Jiminny MCP Connector
Jiminny MCP Connector
Jiminny MCP Connector
By Galya Dimitrova
By Galya Dimitrova
Read time 12 min
12 min
Views 14
14
Add a reaction
Add a reaction
Epic
Link to Epik in Jira
Document status
DRAFT
Epic
Document status
Link to Epik in Jira
DRAFT
Change emoji Objective
Objective
Change emoji Objective
Enable customers to connect Jiminny data to external AI tools (Claude, OpenAI, Gemini) so it can be used as part of their broader knowledge base and workflows.
Position Jiminny as a
data layer for AI-driven revenue workflows
, not just a standalone product.
👤 Target user...
|
NULL
|
|
21194
|
467
|
19
|
2026-04-15T09:56:57.209806+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776247017209_m1.jpg...
|
Firefox
|
balsamo84's Account — Work
|
True
|
store.steampowered.com/account/authorizeddevices
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Team - Backlog - Jira
Service-Desk - Queu Platform Team - Backlog - Jira
Service-Desk - Queues - Platform team - Service space - Jira
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Inbox (1,552) - [EMAIL] - Jiminny Mail
For you - Confluence
For you - Confluence
Lukas Kovalik - Time Off
Lukas Kovalik - Time Off
Product Growth Platform | Userpilot
Product Growth Platform | Userpilot
Userpilot
Userpilot
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Jiminny
Jiminny
balsamo84's Account
balsamo84's Account
Close tab
New Tab
New Tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Link to the Steam Homepage
STORE
STORE
COMMUNITY
COMMUNITY
KOVALIKLUKAS
KOVALIKLUKAS
CHAT
CHAT
SUPPORT
SUPPORT
Install Steam
Install Steam
Notifications
kovaliklukas
View your profile
Browse
Browse
Recommendations
Recommendations
Categories
Categories
Hardware
Hardware
Ways to Play
Ways to Play
Special Sections
Special Sections
Search the store
Search
Wishlist 1
Wishlist
1
Home
Home
>
Account
Account
balsamo84's Account
balsamo84's Account
Account details
Account details
Store preferences
Store preferences
Family Management
Family Management
Security & Devices
Security & Devices
Language Preferences
Language Preferences
Data & Browsing
Data & Browsing
Notification Settings
Notification Settings
Playtests
Playtests
Account Security
Steam Guard helps protect the value in your account from unauthorized access. You'll be asked to verify your sign in with the Steam Mobile App.
Steam Guard FAQ
Steam Guard FAQ
Steam Guard Mobile Authenticator
Remove
Remove
Mobile device - "iPhone 15 Pro" Roosendaal, NL Approved a login less than a minute ago
Mobile device
- "iPhone 15 Pro"
Roosendaal, NL
Approved a login less than a minute ago
Move authenticator to a new device
Move authenticator to a new device
Help, I lost my authenticator
Help, I lost my authenticator
View FAQ
View FAQ
Steam account:
balsamo84
Change password
Change password
Phone number:
None
Manage phone number
Manage phone number
Email address:
[EMAIL]
Status:
Verified
Change email address
Change email address
Backup codes are single-use Steam Guard codes just for your account.
Get backup codes
Get [BACKUP_CODE]the PCs, mobile devices, and web browsers where your account has recently been signed in.
To end all current sessions and sign you out of all devices and browsers, select "Sign out everywhere" below.
Active now
Mobile device - "iPhone 15 Pro" Roosendaal, NL
Mobile device
- "iPhone 15 Pro"
Roosendaal, NL
Web browser - "Firefox on Mac OS" THIS DEVICE Lovech, BG
Web browser
- "Firefox on Mac OS"
THIS DEVICE
Lovech, BG
Web browser - "Firefox on Mac OS" Lovech, BG
Web browser
- "Firefox on Mac OS"
Lovech, BG
Recently seen devices
PC Steam Client - "Boosteroid" Velizy-Villacoublay, FR Last active: 17 hours ago
PC Steam Client
- "Boosteroid"
Velizy-Villacoublay, FR
Last active:
17 hours ago
Mobile device - "iPhone 15 Pro" Sofia, BG Last active: 2 weeks ago
Mobile device
- "iPhone 15 Pro"
Sofia, BG
Last active:
2 weeks ago
PC Steam Client - "lukass-macbook-air" Sofia, BG Last active: 2 weeks ago
PC Steam Client
- "lukass-macbook-air"
Sofia, BG
Last active:
2 weeks ago
PC Steam Client - "Boosteroid" Voluntari, RO Last active: 5 weeks ago
PC Steam Client
- "Boosteroid"
Voluntari, RO
Last active:
5 weeks ago
PC Steam Client - "Boosteroid" Voluntari, RO Last active: 5 weeks ago
PC Steam Client
- "Boosteroid"
Voluntari, RO
Last active:
5 weeks ago
PC Steam Client - "Boosteroid" Voluntari, RO Last active: 5 weeks ago
PC Steam Client...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Team - Backlog - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Service-Desk - Queues - Platform team - Service space - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox (1,552) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"For you - Confluence","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"For you - Confluence","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Lukas Kovalik - Time Off","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Lukas Kovalik - Time Off","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Product Growth Platform | Userpilot","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product Growth Platform | Userpilot","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Userpilot","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Userpilot","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"balsamo84's Account","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"balsamo84's Account","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Link to the Steam Homepage","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"STORE","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"STORE","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"COMMUNITY","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"COMMUNITY","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"KOVALIKLUKAS","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"KOVALIKLUKAS","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"CHAT","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CHAT","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUPPORT","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SUPPORT","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Install Steam","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Install Steam","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Notifications","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"kovaliklukas","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"View your profile","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Browse","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Browse","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Recommendations","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Recommendations","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Categories","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Categories","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Hardware","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Hardware","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Ways to Play","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Ways to Play","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Special Sections","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Special Sections","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Search the store","depth":11,"help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Wishlist 1","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Wishlist","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Home","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Home","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":">","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Account","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Account","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"balsamo84's Account","depth":7,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"balsamo84's Account","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Account details","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Account details","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Store preferences","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Store preferences","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Family Management","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Family Management","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Security & Devices","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Security & Devices","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Language Preferences","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Language Preferences","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Data & Browsing","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Data & Browsing","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Notification Settings","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Notification Settings","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Playtests","depth":8,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Playtests","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Account Security","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Steam Guard helps protect the value in your account from unauthorized access. You'll be asked to verify your sign in with the Steam Mobile App.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Steam Guard FAQ","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Steam Guard FAQ","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Steam Guard Mobile Authenticator","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Remove","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Remove","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Mobile device - \"iPhone 15 Pro\" Roosendaal, NL Approved a login less than a minute ago","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile device","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"iPhone 15 Pro\"","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Roosendaal, NL","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Approved a login less than a minute ago","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Move authenticator to a new device","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Move authenticator to a new device","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Help, I lost my authenticator","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Help, I lost my authenticator","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"View FAQ","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"View FAQ","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Steam account:","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"balsamo84","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change password","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":true,"is_selected":false},{"role":"AXStaticText","text":"Change password","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Phone number:","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"None","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Manage phone number","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Manage phone number","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Email address:","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"kovaliklukas@gmail.com","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Status:","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Verified","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Change email address","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Change email address","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Backup codes are single-use Steam Guard codes just for your account.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Get backup codes","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Get backup codes","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authorized Devices","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This section lists the PCs, mobile devices, and web browsers where your account has recently been signed in.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"To end all current sessions and sign you out of all devices and browsers, select \"Sign out everywhere\" below.","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Active now","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Mobile device - \"iPhone 15 Pro\" Roosendaal, NL","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile device","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"iPhone 15 Pro\"","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Roosendaal, NL","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Web browser - \"Firefox on Mac OS\" THIS DEVICE Lovech, BG","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Web browser","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"Firefox on Mac OS\"","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"THIS DEVICE","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Lovech, BG","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Web browser - \"Firefox on Mac OS\" Lovech, BG","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Web browser","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"Firefox on Mac OS\"","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Lovech, BG","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recently seen devices","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"PC Steam Client - \"Boosteroid\" Velizy-Villacoublay, FR Last active: 17 hours ago","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"PC Steam Client","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"Boosteroid\"","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Velizy-Villacoublay, FR","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last active:","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"17 hours ago","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Mobile device - \"iPhone 15 Pro\" Sofia, BG Last active: 2 weeks ago","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile device","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"iPhone 15 Pro\"","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sofia, BG","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last active:","depth":12,"bounds":{"left":0.34583333,"top":0.0,"width":0.050694443,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 weeks ago","depth":12,"bounds":{"left":0.39652777,"top":0.0,"width":0.05277778,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"PC Steam Client - \"lukass-macbook-air\" Sofia, BG Last active: 2 weeks ago","depth":10,"bounds":{"left":0.30625,"top":0.0,"width":0.6458333,"height":0.08},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"PC Steam Client","depth":12,"bounds":{"left":0.34513888,"top":0.0,"width":0.07152778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"lukass-macbook-air\"","depth":12,"bounds":{"left":0.41666666,"top":0.0,"width":0.108333334,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sofia, BG","depth":11,"bounds":{"left":0.86944443,"top":0.0,"width":0.04097222,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last active:","depth":12,"bounds":{"left":0.34513888,"top":0.0055555557,"width":0.05,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 weeks ago","depth":12,"bounds":{"left":0.3951389,"top":0.0055555557,"width":0.05277778,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"PC Steam Client - \"Boosteroid\" Voluntari, RO Last active: 5 weeks ago","depth":10,"bounds":{"left":0.30625,"top":0.05111111,"width":0.6458333,"height":0.08111111},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"PC Steam Client","depth":12,"bounds":{"left":0.34513888,"top":0.07111111,"width":0.07152778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"Boosteroid\"","depth":12,"bounds":{"left":0.41666666,"top":0.07111111,"width":0.068055555,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Voluntari, RO","depth":11,"bounds":{"left":0.8506944,"top":0.07111111,"width":0.059722222,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last active:","depth":12,"bounds":{"left":0.34513888,"top":0.093333334,"width":0.05,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5 weeks ago","depth":12,"bounds":{"left":0.3951389,"top":0.093333334,"width":0.05277778,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"PC Steam Client - \"Boosteroid\" Voluntari, RO Last active: 5 weeks ago","depth":10,"bounds":{"left":0.30625,"top":0.14,"width":0.6458333,"height":0.08},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"PC Steam Client","depth":12,"bounds":{"left":0.34513888,"top":0.15888889,"width":0.07152778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- \"Boosteroid\"","depth":12,"bounds":{"left":0.41666666,"top":0.15888889,"width":0.068055555,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Voluntari, RO","depth":11,"bounds":{"left":0.8506944,"top":0.15888889,"width":0.059722222,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last active:","depth":12,"bounds":{"left":0.34513888,"top":0.18222222,"width":0.05,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5 weeks ago","depth":12,"bounds":{"left":0.3951389,"top":0.18222222,"width":0.05277778,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"PC Steam Client - \"Boosteroid\" Voluntari, RO Last active: 5 weeks ago","depth":10,"bounds":{"left":0.30625,"top":0.22777778,"width":0.6458333,"height":0.08},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"PC Steam Client","depth":12,"bounds":{"left":0.34513888,"top":0.24666667,"width":0.07152778,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-4334050880248785329
|
7933728863573251487
|
click
|
accessibility
|
NULL
|
Platform Team - Backlog - Jira
Service-Desk - Queu Platform Team - Backlog - Jira
Service-Desk - Queues - Platform team - Service space - Jira
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Inbox (1,552) - [EMAIL] - Jiminny Mail
For you - Confluence
For you - Confluence
Lukas Kovalik - Time Off
Lukas Kovalik - Time Off
Product Growth Platform | Userpilot
Product Growth Platform | Userpilot
Userpilot
Userpilot
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Jiminny
Jiminny
balsamo84's Account
balsamo84's Account
Close tab
New Tab
New Tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Link to the Steam Homepage
STORE
STORE
COMMUNITY
COMMUNITY
KOVALIKLUKAS
KOVALIKLUKAS
CHAT
CHAT
SUPPORT
SUPPORT
Install Steam
Install Steam
Notifications
kovaliklukas
View your profile
Browse
Browse
Recommendations
Recommendations
Categories
Categories
Hardware
Hardware
Ways to Play
Ways to Play
Special Sections
Special Sections
Search the store
Search
Wishlist 1
Wishlist
1
Home
Home
>
Account
Account
balsamo84's Account
balsamo84's Account
Account details
Account details
Store preferences
Store preferences
Family Management
Family Management
Security & Devices
Security & Devices
Language Preferences
Language Preferences
Data & Browsing
Data & Browsing
Notification Settings
Notification Settings
Playtests
Playtests
Account Security
Steam Guard helps protect the value in your account from unauthorized access. You'll be asked to verify your sign in with the Steam Mobile App.
Steam Guard FAQ
Steam Guard FAQ
Steam Guard Mobile Authenticator
Remove
Remove
Mobile device - "iPhone 15 Pro" Roosendaal, NL Approved a login less than a minute ago
Mobile device
- "iPhone 15 Pro"
Roosendaal, NL
Approved a login less than a minute ago
Move authenticator to a new device
Move authenticator to a new device
Help, I lost my authenticator
Help, I lost my authenticator
View FAQ
View FAQ
Steam account:
balsamo84
Change password
Change password
Phone number:
None
Manage phone number
Manage phone number
Email address:
[EMAIL]
Status:
Verified
Change email address
Change email address
Backup codes are single-use Steam Guard codes just for your account.
Get backup codes
Get [BACKUP_CODE]the PCs, mobile devices, and web browsers where your account has recently been signed in.
To end all current sessions and sign you out of all devices and browsers, select "Sign out everywhere" below.
Active now
Mobile device - "iPhone 15 Pro" Roosendaal, NL
Mobile device
- "iPhone 15 Pro"
Roosendaal, NL
Web browser - "Firefox on Mac OS" THIS DEVICE Lovech, BG
Web browser
- "Firefox on Mac OS"
THIS DEVICE
Lovech, BG
Web browser - "Firefox on Mac OS" Lovech, BG
Web browser
- "Firefox on Mac OS"
Lovech, BG
Recently seen devices
PC Steam Client - "Boosteroid" Velizy-Villacoublay, FR Last active: 17 hours ago
PC Steam Client
- "Boosteroid"
Velizy-Villacoublay, FR
Last active:
17 hours ago
Mobile device - "iPhone 15 Pro" Sofia, BG Last active: 2 weeks ago
Mobile device
- "iPhone 15 Pro"
Sofia, BG
Last active:
2 weeks ago
PC Steam Client - "lukass-macbook-air" Sofia, BG Last active: 2 weeks ago
PC Steam Client
- "lukass-macbook-air"
Sofia, BG
Last active:
2 weeks ago
PC Steam Client - "Boosteroid" Voluntari, RO Last active: 5 weeks ago
PC Steam Client
- "Boosteroid"
Voluntari, RO
Last active:
5 weeks ago
PC Steam Client - "Boosteroid" Voluntari, RO Last active: 5 weeks ago
PC Steam Client
- "Boosteroid"
Voluntari, RO
Last active:
5 weeks ago
PC Steam Client - "Boosteroid" Voluntari, RO Last active: 5 weeks ago
PC Steam Client...
|
NULL
|
|
23985
|
519
|
33
|
2026-04-15T11:53:46.841679+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776254026841_m2.jpg...
|
Finder
|
data
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Favourites
jiminny
AirDrop
Recents
Applications
Do Favourites
jiminny
AirDrop
Recents
Applications
Documents
Desktop
Downloads
lukas
iCloud
iCloud Drive
Sync folder
Locations
DXP4800PLUS-B5F8
Eject
Network
Tags
CRM
Orange
Red
Yellow
Green
Blue
Purple
All Tags…
Name
Date Modified
Size
Kind
data
Today at 9:58
1,91 GB
Folder
pending-transcriptions
9 Apr 2026 at 20:05
Zero bytes
Folder
screenpipe.db
11 Apr 2026 at 15:03
Zero bytes
Document
Name
Date Modified
Size
Kind
1 of 3 selected, 38,35 GB available
data...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Favourites","depth":6,"bounds":{"left":0.10976563,"top":0.45277777,"width":0.0671875,"height":0.013194445},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"jiminny","depth":6,"bounds":{"left":0.119140625,"top":0.47013888,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"AirDrop","depth":6,"bounds":{"left":0.119140625,"top":0.48958334,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Recents","depth":6,"bounds":{"left":0.119140625,"top":0.5090278,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Applications","depth":6,"bounds":{"left":0.119140625,"top":0.52847224,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Documents","depth":6,"bounds":{"left":0.119140625,"top":0.54791665,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Desktop","depth":6,"bounds":{"left":0.119140625,"top":0.5673611,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Downloads","depth":6,"bounds":{"left":0.119140625,"top":0.5868056,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":6,"bounds":{"left":0.119140625,"top":0.60625,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"iCloud","depth":6,"bounds":{"left":0.10976563,"top":0.63055557,"width":0.0671875,"height":0.013194445},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"iCloud Drive","depth":6,"bounds":{"left":0.119140625,"top":0.6479167,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Sync folder","depth":6,"bounds":{"left":0.119140625,"top":0.66736114,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Locations","depth":6,"bounds":{"left":0.10976563,"top":0.69166666,"width":0.0671875,"height":0.013194445},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"DXP4800PLUS-B5F8","depth":6,"bounds":{"left":0.119140625,"top":0.70902777,"width":0.0453125,"height":0.011111111},"role_description":"text"},{"role":"AXButton","text":"Eject","depth":6,"bounds":{"left":0.16523437,"top":0.7104167,"width":0.005078125,"height":0.008333334},"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"Network","depth":6,"bounds":{"left":0.119140625,"top":0.72847223,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Tags","depth":6,"bounds":{"left":0.10976563,"top":0.75277776,"width":0.0671875,"height":0.013194445},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"CRM","depth":6,"bounds":{"left":0.119140625,"top":0.77013886,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Orange","depth":6,"bounds":{"left":0.119140625,"top":0.7895833,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Red","depth":6,"bounds":{"left":0.119140625,"top":0.8090278,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Yellow","depth":6,"bounds":{"left":0.119140625,"top":0.8284722,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Green","depth":6,"bounds":{"left":0.119140625,"top":0.84791666,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Blue","depth":6,"bounds":{"left":0.119140625,"top":0.8673611,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Purple","depth":6,"bounds":{"left":0.119140625,"top":0.88680553,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"All Tags…","depth":6,"bounds":{"left":0.119140625,"top":0.90625,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Name","depth":7,"bounds":{"left":0.2015625,"top":0.45694444,"width":0.013671875,"height":0.009722223},"role_description":"text"},{"role":"AXStaticText","text":"Date Modified","depth":7,"bounds":{"left":0.309375,"top":0.45694444,"width":0.03046875,"height":0.009722223},"role_description":"text"},{"role":"AXStaticText","text":"Size","depth":7,"bounds":{"left":0.38007814,"top":0.45694444,"width":0.010546875,"height":0.009722223},"role_description":"text"},{"role":"AXStaticText","text":"Kind","depth":7,"bounds":{"left":0.41796875,"top":0.45694444,"width":0.0109375,"height":0.009722223},"role_description":"text"},{"role":"AXTextField","text":"data","depth":7,"bounds":{"left":0.2015625,"top":0.47708333,"width":0.013671875,"height":0.011111111},"value":"data","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Today at 9:58","depth":7,"bounds":{"left":0.309375,"top":0.47708333,"width":0.06679688,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"1,91 GB","depth":7,"bounds":{"left":0.39296874,"top":0.47708333,"width":0.02109375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":7,"bounds":{"left":0.41796875,"top":0.47708333,"width":0.016796876,"height":0.011111111},"role_description":"text"},{"role":"AXTextField","text":"pending-transcriptions","depth":7,"bounds":{"left":0.2015625,"top":0.49097222,"width":0.057421874,"height":0.011111111},"value":"pending-transcriptions","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Apr 2026 at 20:05","depth":7,"bounds":{"left":0.309375,"top":0.49097222,"width":0.06679688,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Zero bytes","depth":7,"bounds":{"left":0.38554686,"top":0.49097222,"width":0.028515626,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":7,"bounds":{"left":0.41796875,"top":0.49097222,"width":0.016796876,"height":0.011111111},"role_description":"text"},{"role":"AXTextField","text":"screenpipe.db","depth":7,"bounds":{"left":0.2015625,"top":0.5048611,"width":0.037109375,"height":0.011111111},"value":"screenpipe.db","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"11 Apr 2026 at 15:03","depth":7,"bounds":{"left":0.309375,"top":0.5048611,"width":0.06679688,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Zero bytes","depth":7,"bounds":{"left":0.38554686,"top":0.5048611,"width":0.028515626,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Document","depth":7,"bounds":{"left":0.41796875,"top":0.5048611,"width":0.027734375,"height":0.011111111},"role_description":"text"},{"role":"AXButton","text":"Name","depth":6,"bounds":{"left":0.18789062,"top":0.45277777,"width":0.11953125,"height":0.019444445},"role_description":"sort button","subrole":"AXSortButton","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Date Modified","depth":6,"bounds":{"left":0.30742186,"top":0.45277777,"width":0.07070313,"height":0.019444445},"role_description":"sort button","subrole":"AXSortButton","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Size","depth":6,"bounds":{"left":0.378125,"top":0.45277777,"width":0.037890624,"height":0.019444445},"role_description":"sort button","subrole":"AXSortButton","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Kind","depth":6,"bounds":{"left":0.41601562,"top":0.45277777,"width":0.047265626,"height":0.019444445},"role_description":"sort button","subrole":"AXSortButton","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"1 of 3 selected, 38,35 GB available","depth":2,"bounds":{"left":0.28320312,"top":0.7048611,"width":0.07695313,"height":0.009722223},"automation_id":"_NS:34","role_description":"text"},{"role":"AXStaticText","text":"data","depth":1,"bounds":{"left":0.21679688,"top":0.41666666,"width":0.095703125,"height":0.036111113},"role_description":"text"}]...
|
-6812797777607446068
|
7931437546112348006
|
click
|
accessibility
|
NULL
|
Favourites
jiminny
AirDrop
Recents
Applications
Do Favourites
jiminny
AirDrop
Recents
Applications
Documents
Desktop
Downloads
lukas
iCloud
iCloud Drive
Sync folder
Locations
DXP4800PLUS-B5F8
Eject
Network
Tags
CRM
Orange
Red
Yellow
Green
Blue
Purple
All Tags…
Name
Date Modified
Size
Kind
data
Today at 9:58
1,91 GB
Folder
pending-transcriptions
9 Apr 2026 at 20:05
Zero bytes
Folder
screenpipe.db
11 Apr 2026 at 15:03
Zero bytes
Document
Name
Date Modified
Size
Kind
1 of 3 selected, 38,35 GB available
data...
|
23984
|
|
24011
|
519
|
44
|
2026-04-15T11:54:27.401498+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776254067401_m2.jpg...
|
Finder
|
data
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Favourites
jiminny
AirDrop
Recents
Applications
Do Favourites
jiminny
AirDrop
Recents
Applications
Documents
Desktop
Downloads
lukas
iCloud
iCloud Drive
Sync folder
Locations
DXP4800PLUS-B5F8
Eject
Network
Tags
CRM
Orange
Red
Yellow
Green
Blue
Purple
All Tags…
Name
Date Modified
Size
Kind
data
Today at 9:58
1,91 GB
Folder
pending-transcriptions
9 Apr 2026 at 20:05
Zero bytes
Folder
screenpipe.db
11 Apr 2026 at 15:03
Zero bytes
Document
Name
Date Modified
Size
Kind
9 items, 38,34 GB available...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Favourites","depth":6,"bounds":{"left":0.10976563,"top":0.45277777,"width":0.0671875,"height":0.013194445},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"jiminny","depth":6,"bounds":{"left":0.119140625,"top":0.47013888,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"AirDrop","depth":6,"bounds":{"left":0.119140625,"top":0.48958334,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Recents","depth":6,"bounds":{"left":0.119140625,"top":0.5090278,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Applications","depth":6,"bounds":{"left":0.119140625,"top":0.52847224,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Documents","depth":6,"bounds":{"left":0.119140625,"top":0.54791665,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Desktop","depth":6,"bounds":{"left":0.119140625,"top":0.5673611,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Downloads","depth":6,"bounds":{"left":0.119140625,"top":0.5868056,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":6,"bounds":{"left":0.119140625,"top":0.60625,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"iCloud","depth":6,"bounds":{"left":0.10976563,"top":0.63055557,"width":0.0671875,"height":0.013194445},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"iCloud Drive","depth":6,"bounds":{"left":0.119140625,"top":0.6479167,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Sync folder","depth":6,"bounds":{"left":0.119140625,"top":0.66736114,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Locations","depth":6,"bounds":{"left":0.10976563,"top":0.69166666,"width":0.0671875,"height":0.013194445},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"DXP4800PLUS-B5F8","depth":6,"bounds":{"left":0.119140625,"top":0.70902777,"width":0.0453125,"height":0.011111111},"role_description":"text"},{"role":"AXButton","text":"Eject","depth":6,"bounds":{"left":0.16523437,"top":0.7104167,"width":0.005078125,"height":0.008333334},"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"Network","depth":6,"bounds":{"left":0.119140625,"top":0.72847223,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Tags","depth":6,"bounds":{"left":0.10976563,"top":0.75277776,"width":0.0671875,"height":0.013194445},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"CRM","depth":6,"bounds":{"left":0.119140625,"top":0.77013886,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Orange","depth":6,"bounds":{"left":0.119140625,"top":0.7895833,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Red","depth":6,"bounds":{"left":0.119140625,"top":0.8090278,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Yellow","depth":6,"bounds":{"left":0.119140625,"top":0.8284722,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Green","depth":6,"bounds":{"left":0.119140625,"top":0.84791666,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Blue","depth":6,"bounds":{"left":0.119140625,"top":0.8673611,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Purple","depth":6,"bounds":{"left":0.119140625,"top":0.88680553,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"All Tags…","depth":6,"bounds":{"left":0.119140625,"top":0.90625,"width":0.05234375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Name","depth":7,"bounds":{"left":0.2015625,"top":0.45694444,"width":0.013671875,"height":0.009722223},"role_description":"text"},{"role":"AXStaticText","text":"Date Modified","depth":7,"bounds":{"left":0.309375,"top":0.45694444,"width":0.03046875,"height":0.009722223},"role_description":"text"},{"role":"AXStaticText","text":"Size","depth":7,"bounds":{"left":0.38007814,"top":0.45694444,"width":0.010546875,"height":0.009722223},"role_description":"text"},{"role":"AXStaticText","text":"Kind","depth":7,"bounds":{"left":0.41796875,"top":0.45694444,"width":0.0109375,"height":0.009722223},"role_description":"text"},{"role":"AXTextField","text":"data","depth":7,"bounds":{"left":0.2015625,"top":0.47708333,"width":0.013671875,"height":0.011111111},"value":"data","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Today at 9:58","depth":7,"bounds":{"left":0.309375,"top":0.47708333,"width":0.06679688,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"1,91 GB","depth":7,"bounds":{"left":0.39296874,"top":0.47708333,"width":0.02109375,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":7,"bounds":{"left":0.41796875,"top":0.47708333,"width":0.016796876,"height":0.011111111},"role_description":"text"},{"role":"AXTextField","text":"pending-transcriptions","depth":7,"bounds":{"left":0.2015625,"top":0.57430553,"width":0.057421874,"height":0.011111111},"value":"pending-transcriptions","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Apr 2026 at 20:05","depth":7,"bounds":{"left":0.309375,"top":0.57430553,"width":0.06679688,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Zero bytes","depth":7,"bounds":{"left":0.38554686,"top":0.57430553,"width":0.028515626,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":7,"bounds":{"left":0.41796875,"top":0.57430553,"width":0.016796876,"height":0.011111111},"role_description":"text"},{"role":"AXTextField","text":"screenpipe.db","depth":7,"bounds":{"left":0.2015625,"top":0.58819443,"width":0.037109375,"height":0.011111111},"value":"screenpipe.db","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"11 Apr 2026 at 15:03","depth":7,"bounds":{"left":0.309375,"top":0.58819443,"width":0.06679688,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Zero bytes","depth":7,"bounds":{"left":0.38554686,"top":0.58819443,"width":0.028515626,"height":0.011111111},"role_description":"text"},{"role":"AXStaticText","text":"Document","depth":7,"bounds":{"left":0.41796875,"top":0.58819443,"width":0.027734375,"height":0.011111111},"role_description":"text"},{"role":"AXButton","text":"Name","depth":6,"bounds":{"left":0.18789062,"top":0.45277777,"width":0.11953125,"height":0.019444445},"role_description":"sort button","subrole":"AXSortButton","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Date Modified","depth":6,"bounds":{"left":0.30742186,"top":0.45277777,"width":0.07070313,"height":0.019444445},"role_description":"sort button","subrole":"AXSortButton","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Size","depth":6,"bounds":{"left":0.378125,"top":0.45277777,"width":0.037890624,"height":0.019444445},"role_description":"sort button","subrole":"AXSortButton","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"Kind","depth":6,"bounds":{"left":0.41601562,"top":0.45277777,"width":0.047265626,"height":0.019444445},"role_description":"sort button","subrole":"AXSortButton","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"9 items, 38,34 GB available","depth":2,"bounds":{"left":0.29101562,"top":0.7048611,"width":0.061328124,"height":0.009722223},"automation_id":"_NS:34","role_description":"text"}]...
|
8297445638273021270
|
7931437407599979310
|
click
|
accessibility
|
NULL
|
Favourites
jiminny
AirDrop
Recents
Applications
Do Favourites
jiminny
AirDrop
Recents
Applications
Documents
Desktop
Downloads
lukas
iCloud
iCloud Drive
Sync folder
Locations
DXP4800PLUS-B5F8
Eject
Network
Tags
CRM
Orange
Red
Yellow
Green
Blue
Purple
All Tags…
Name
Date Modified
Size
Kind
data
Today at 9:58
1,91 GB
Folder
pending-transcriptions
9 Apr 2026 at 20:05
Zero bytes
Folder
screenpipe.db
11 Apr 2026 at 15:03
Zero bytes
Document
Name
Date Modified
Size
Kind
9 items, 38,34 GB available...
|
NULL
|
|
52254
|
1129
|
39
|
2026-04-20T06:47:27.080656+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776667647080_m2.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.016123671,"height":-0.051875472},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.27094415,"top":1.0,"width":0.004986702,"height":-0.051875472},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.27310506,"top":1.0,"width":0.010638298,"height":-0.086193085},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Nikolay Yankov (Presenting)","depth":12,"bounds":{"left":0.30634972,"top":1.0,"width":0.059507977,"height":-0.072625756},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nikolay Yankov (Presenting)","depth":13,"bounds":{"left":0.30634972,"top":1.0,"width":0.059507977,"height":-0.07342374},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"People","depth":14,"bounds":{"left":0.69481385,"top":1.0,"width":0.019614361,"height":-0.06424582},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":21,"bounds":{"left":0.7081117,"top":1.0,"width":0.0023271276,"height":-0.072625756},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":13,"bounds":{"left":0.71708775,"top":1.0,"width":0.011968086,"height":-0.06424582},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":16,"bounds":{"left":0.7184175,"top":1.0,"width":0.043550532,"height":-0.072625756},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":21,"bounds":{"left":0.7330452,"top":1.0,"width":0.013464096,"height":-0.072625756},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":20,"bounds":{"left":0.73204786,"top":1.0,"width":0.011303191,"height":-0.065043926},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-5247432300953325732
|
7930973479653668486
|
click
|
accessibility
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
NULL
|
|
52317
|
1131
|
24
|
2026-04-20T06:50:40.656525+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776667840656_m2.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.016123671,"height":-0.051875472},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.27094415,"top":1.0,"width":0.004986702,"height":-0.051875472},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.27310506,"top":1.0,"width":0.010638298,"height":-0.086193085},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Nikolay Yankov (Presenting)","depth":12,"bounds":{"left":0.30634972,"top":1.0,"width":0.059507977,"height":-0.072625756},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nikolay Yankov (Presenting)","depth":13,"bounds":{"left":0.30634972,"top":1.0,"width":0.059507977,"height":-0.07342374},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"People","depth":14,"bounds":{"left":0.69481385,"top":1.0,"width":0.019614361,"height":-0.06424582},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":21,"bounds":{"left":0.7081117,"top":1.0,"width":0.0023271276,"height":-0.072625756},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":13,"bounds":{"left":0.71708775,"top":1.0,"width":0.011968086,"height":-0.06424582},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":16,"bounds":{"left":0.7184175,"top":1.0,"width":0.043550532,"height":-0.072625756},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":21,"bounds":{"left":0.7330452,"top":1.0,"width":0.013464096,"height":-0.072625756},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":20,"bounds":{"left":0.73204786,"top":1.0,"width":0.011303191,"height":-0.065043926},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-5247432300953325732
|
7930973479653668486
|
visual_change
|
accessibility
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
NULL
|
|
52332
|
1130
|
44
|
2026-04-20T06:52:22.583583+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776667942583_m1.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.0,"top":0.072222225,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.072222225,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.005902778,"top":0.12,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.7977778,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.8411111,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.8794444,"width":0.033680554,"height":0.03888889},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.0,"top":0.91833335,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.95666665,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Nikolay Yankov (Presenting)","depth":12,"bounds":{"left":0.07534722,"top":0.101111114,"width":0.124305554,"height":0.022222223},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nikolay Yankov (Presenting)","depth":13,"bounds":{"left":0.07534722,"top":0.10222222,"width":0.124305554,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"People","depth":14,"bounds":{"left":0.88680553,"top":0.08944444,"width":0.04097222,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":21,"bounds":{"left":0.9145833,"top":0.101111114,"width":0.0048611113,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":13,"bounds":{"left":0.93333334,"top":0.08944444,"width":0.025,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":16,"bounds":{"left":0.9361111,"top":0.101111114,"width":0.06388891,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":21,"bounds":{"left":0.96666664,"top":0.101111114,"width":0.028125,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":20,"bounds":{"left":0.96458334,"top":0.090555556,"width":0.023611112,"height":0.037777778},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-5247432300953325732
|
7930973479653668486
|
visual_change
|
accessibility
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
NULL
|
|
68883
|
1566
|
10
|
2026-04-22T06:52:00.498584+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776840720498_m1.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.0,"top":0.072222225,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.072222225,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.005902778,"top":0.12,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.7977778,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.8411111,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.8794444,"width":0.033680554,"height":0.03888889},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.0,"top":0.91833335,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.95666665,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Nikolay Yankov (Presenting)","depth":12,"bounds":{"left":0.07534722,"top":0.101111114,"width":0.124305554,"height":0.022222223},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nikolay Yankov (Presenting)","depth":13,"bounds":{"left":0.07534722,"top":0.10222222,"width":0.124305554,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"People","depth":14,"bounds":{"left":0.88680553,"top":0.08944444,"width":0.04097222,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":21,"bounds":{"left":0.9145833,"top":0.101111114,"width":0.0048611113,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":13,"bounds":{"left":0.93333334,"top":0.08944444,"width":0.025,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":16,"bounds":{"left":0.9361111,"top":0.101111114,"width":0.06388891,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":16,"bounds":{"left":0.96666664,"top":0.101111114,"width":0.028125,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":16,"bounds":{"left":0.96458334,"top":0.090555556,"width":0.023611112,"height":0.037777778},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-5247432300953325732
|
7930973479653668486
|
visual_change
|
accessibility
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
68880
|
|
68901
|
1568
|
5
|
2026-04-22T06:55:55.823167+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776840955823_m1.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.0,"top":0.072222225,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.072222225,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.005902778,"top":0.12,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.7977778,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.8411111,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.8794444,"width":0.033680554,"height":0.03888889},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.0,"top":0.91833335,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.95666665,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Nikolay Yankov (Presenting)","depth":12,"bounds":{"left":0.07534722,"top":0.101111114,"width":0.124305554,"height":0.022222223},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nikolay Yankov (Presenting)","depth":13,"bounds":{"left":0.07534722,"top":0.10222222,"width":0.124305554,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"People","depth":14,"bounds":{"left":0.88680553,"top":0.08944444,"width":0.04097222,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":21,"bounds":{"left":0.9145833,"top":0.101111114,"width":0.0048611113,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":13,"bounds":{"left":0.93333334,"top":0.08944444,"width":0.025,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":16,"bounds":{"left":0.9361111,"top":0.101111114,"width":0.06388891,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":16,"bounds":{"left":0.96666664,"top":0.101111114,"width":0.028125,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":16,"bounds":{"left":0.96458334,"top":0.090555556,"width":0.023611112,"height":0.037777778},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-5247432300953325732
|
7930973479653668486
|
visual_change
|
accessibility
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
68899
|
|
68906
|
1568
|
8
|
2026-04-22T06:56:43.702647+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776841003702_m1.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.0,"top":0.072222225,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.072222225,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.005902778,"top":0.12,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.7977778,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.8411111,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.8794444,"width":0.033680554,"height":0.03888889},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.0,"top":0.91833335,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.95666665,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Nikolay Yankov (Presenting)","depth":12,"bounds":{"left":0.07534722,"top":0.101111114,"width":0.124305554,"height":0.022222223},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nikolay Yankov (Presenting)","depth":13,"bounds":{"left":0.07534722,"top":0.10222222,"width":0.124305554,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"People","depth":14,"bounds":{"left":0.88680553,"top":0.08944444,"width":0.04097222,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":21,"bounds":{"left":0.9145833,"top":0.101111114,"width":0.0048611113,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":13,"bounds":{"left":0.93333334,"top":0.08944444,"width":0.025,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":16,"bounds":{"left":0.9361111,"top":0.101111114,"width":0.06388891,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":16,"bounds":{"left":0.96666664,"top":0.101111114,"width":0.028125,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":16,"bounds":{"left":0.96458334,"top":0.090555556,"width":0.023611112,"height":0.037777778},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-5247432300953325732
|
7930973479653668486
|
app_switch
|
accessibility
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Nikolay Yankov (Presenting)
Nikolay Yankov (Presenting)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini...
|
NULL
|
|
15515
|
349
|
13
|
2026-04-14T14:49:19.359769+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-14/1776 /Users/lukas/.screenpipe/data/data/2026-04-14/1776178159359_m2.jpg...
|
PhpStorm
|
Push Commits to app
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
JY-18909-automated-reports-ask-jiminny → origin : JY-18909-automated-reports-ask-jiminny → origin : JY-18909-automated-reports-ask-jiminny
JY-18909 modify send automated report command to support immediate send for specific result
Show Diff
Group By
Jump to Source
Show Details
Expand All
Collapse All
3 files, folder
app 2 files, folder
Console/Commands/Reports 1 file, folder
AutomatedReportsCommand.php, class
Repositories 1 file, folder
AutomatedReportsRepository.php, class
tests/Unit/Console/Commands/Reports 1 file, folder
AutomatedReportsCommandTest.php, class
app 2 files, folder
Console/Commands/Reports 1 file, folder
AutomatedReportsCommand.php, class
Repositories 1 file, folder
AutomatedReportsRepository.php, class
Console/Commands/Reports 1 file, folder
AutomatedReportsCommand.php, class
AutomatedReportsCommand.php, class
Repositories 1 file, folder
AutomatedReportsRepository.php, class
AutomatedReportsRepository.php, class
tests/Unit/Console/Commands/Reports 1 file, folder
AutomatedReportsCommandTest.php, class
AutomatedReportsCommandTest.php, class
For Commit and Push to non-protected branches, preview commits before push
Help
Push tags:
All
Current Branch
Cancel
Push
Push
Push Commits to app...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"JY-18909-automated-reports-ask-jiminny → origin : JY-18909-automated-reports-ask-jiminny","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.23789063,"height":0.0},"role_description":"text"},{"role":"AXStaticText","text":"JY-18909 modify send automated report command to support immediate send for specific result","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.23789063,"height":0.0},"role_description":"text"},{"role":"AXButton","text":"Show Diff","depth":2,"bounds":{"left":0.6363281,"top":0.31041667,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Group By","depth":2,"bounds":{"left":0.64921874,"top":0.31041667,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Jump to Source","depth":2,"bounds":{"left":0.659375,"top":0.31041667,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Details","depth":2,"bounds":{"left":0.67226565,"top":0.31041667,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand All","depth":2,"bounds":{"left":0.76875,"top":0.31041667,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":2,"bounds":{"left":0.7789062,"top":0.31041667,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3 files, folder","depth":4,"bounds":{"left":0.6453125,"top":0.33055556,"width":0.0265625,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"app 2 files, folder","depth":5,"bounds":{"left":0.6527344,"top":0.34583333,"width":0.03515625,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"Console/Commands/Reports 1 file, folder","depth":6,"bounds":{"left":0.66015625,"top":0.3611111,"width":0.091796875,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":7,"bounds":{"left":0.6675781,"top":0.37638888,"width":0.08984375,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"Repositories 1 file, folder","depth":6,"bounds":{"left":0.66015625,"top":0.39166668,"width":0.052734375,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRepository.php, class","depth":7,"bounds":{"left":0.6675781,"top":0.40694445,"width":0.09101562,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"tests/Unit/Console/Commands/Reports 1 file, folder","depth":5,"bounds":{"left":0.6527344,"top":0.42222223,"width":0.1171875,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommandTest.php, class","depth":6,"bounds":{"left":0.66015625,"top":0.4375,"width":0.10078125,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"app 2 files, folder","depth":4,"bounds":{"left":0.6527344,"top":0.34583333,"width":0.03515625,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"Console/Commands/Reports 1 file, folder","depth":5,"bounds":{"left":0.66015625,"top":0.3611111,"width":0.091796875,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":6,"bounds":{"left":0.6675781,"top":0.37638888,"width":0.08984375,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"Repositories 1 file, folder","depth":5,"bounds":{"left":0.66015625,"top":0.39166668,"width":0.052734375,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRepository.php, class","depth":6,"bounds":{"left":0.6675781,"top":0.40694445,"width":0.09101562,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"Console/Commands/Reports 1 file, folder","depth":4,"bounds":{"left":0.66015625,"top":0.3611111,"width":0.091796875,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":5,"bounds":{"left":0.6675781,"top":0.37638888,"width":0.08984375,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":4,"bounds":{"left":0.6675781,"top":0.37638888,"width":0.08984375,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"Repositories 1 file, folder","depth":4,"bounds":{"left":0.66015625,"top":0.39166668,"width":0.052734375,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRepository.php, class","depth":5,"bounds":{"left":0.6675781,"top":0.40694445,"width":0.09101562,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRepository.php, class","depth":4,"bounds":{"left":0.6675781,"top":0.40694445,"width":0.09101562,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"tests/Unit/Console/Commands/Reports 1 file, folder","depth":4,"bounds":{"left":0.6527344,"top":0.42222223,"width":0.1171875,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommandTest.php, class","depth":5,"bounds":{"left":0.66015625,"top":0.4375,"width":0.10078125,"height":0.015277778},"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommandTest.php, class","depth":4,"bounds":{"left":0.66015625,"top":0.4375,"width":0.10078125,"height":0.015277778},"role_description":"text"},{"role":"AXCheckBox","text":"For Commit and Push to non-protected branches, preview commits before push","depth":1,"bounds":{"left":0.48398438,"top":0.6263889,"width":0.203125,"height":0.017361112},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Help","depth":1,"bounds":{"left":0.4828125,"top":0.6493056,"width":0.0109375,"height":0.023611112},"help_text":"Show help contents","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Push tags:","depth":1,"bounds":{"left":0.5015625,"top":0.65208334,"width":0.0375,"height":0.017361112},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"All","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Current Branch","depth":6,"role_description":"text"},{"role":"AXButton","text":"Cancel","depth":1,"bounds":{"left":0.71054685,"top":0.6493056,"width":0.03046875,"height":0.023611112},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Push","depth":1,"bounds":{"left":0.7433594,"top":0.6493056,"width":0.042578124,"height":0.023611112},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Push","depth":2,"bounds":{"left":0.7433594,"top":0.6493056,"width":0.03046875,"height":0.023611112},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Push Commits to app","depth":1,"bounds":{"left":0.60664064,"top":0.2923611,"width":0.05546875,"height":0.011111111},"role_description":"text"}]...
|
-2057863433730899750
|
7930390813301194388
|
click
|
accessibility
|
NULL
|
JY-18909-automated-reports-ask-jiminny → origin : JY-18909-automated-reports-ask-jiminny → origin : JY-18909-automated-reports-ask-jiminny
JY-18909 modify send automated report command to support immediate send for specific result
Show Diff
Group By
Jump to Source
Show Details
Expand All
Collapse All
3 files, folder
app 2 files, folder
Console/Commands/Reports 1 file, folder
AutomatedReportsCommand.php, class
Repositories 1 file, folder
AutomatedReportsRepository.php, class
tests/Unit/Console/Commands/Reports 1 file, folder
AutomatedReportsCommandTest.php, class
app 2 files, folder
Console/Commands/Reports 1 file, folder
AutomatedReportsCommand.php, class
Repositories 1 file, folder
AutomatedReportsRepository.php, class
Console/Commands/Reports 1 file, folder
AutomatedReportsCommand.php, class
AutomatedReportsCommand.php, class
Repositories 1 file, folder
AutomatedReportsRepository.php, class
AutomatedReportsRepository.php, class
tests/Unit/Console/Commands/Reports 1 file, folder
AutomatedReportsCommandTest.php, class
AutomatedReportsCommandTest.php, class
For Commit and Push to non-protected branches, preview commits before push
Help
Push tags:
All
Current Branch
Cancel
Push
Push
Push Commits to app...
|
NULL
|
|
891
|
21
|
22
|
2026-04-11T12:33:46.641041+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775910826641_m1.jpg...
|
Firefox
|
Picture-in-Picture
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Close
Send back to tab
39:23 / 54:15
Backward
Paus Close
Send back to tab
39:23 / 54:15
Backward
Pause
Forward
Mute
Fullscreen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Close","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Send back to tab","depth":3,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"39:23 / 54:15","depth":5,"bounds":{"left":1.0,"top":0.0,"width":-0.03680551,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Backward","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Pause","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Forward","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Mute","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Fullscreen","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
3129782928625813698
|
7916199472445601445
|
visual_change
|
hybrid
|
NULL
|
Close
Send back to tab
39:23 / 54:15
Backward
Paus Close
Send back to tab
39:23 / 54:15
Backward
Pause
Forward
Mute
Fullscreen
Firefox}• 0.100% C8 Sat 11 Apr 15:33:46-zshDOCKER0 ₴1"enabled": true,"featured": true,"icon": "O""name": "day-recap""schedule" : "manual""template": true,"title": "Day Recap"DEV (-zsh)O $2APP (-zsh)|• ₴з-zsh• *4-zsh• *5-zsh0 %6-zsh*7L₴81+},"consecutive_failures": 0,"current_execution_id": null,"is_running": false,"last_error": null,"last_run": null,"last_success": null,"prompt_body": "Analyze my screen and audio recordings from today (last 16 hours only). In\nRead screenpipe skill first.In\nUse this exact format: \n\n## Summary\nOne sentence: what I mainly did today. \n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM\")\n- Name specific apps, files, or projects\n\n## Key Moments \n-Important things I saw, said, or heard - with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow - name the app/file/task\n\n## Patterns\n- Apps I used most,topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue])"","raw_content": "---\nschedule: manual\nenabled: true\ntemplate: true\ntitle: Day Recap\ndescription: \"Today's accomplishments, key moments, and unfinished work\"\nicon: \""\nfeatured: true\n---\n\nAnalyze my screen and audio recordings from today (last 16 hours only). In\nRead screenpipe skill first.\n\nUse this exact format: \n\n## Summary\nOne sentence: what I mainly did today. \n\n## Accomplishments\n- Top 3 things I finished, with timestamps (e.g. \"2:30 PM")\n- Name specific apps, files, or projects\n\n## Key Moments\n- Important things I saw, said, or heard - with timestamps\n\n## Unfinished Work\n- What I should continue tomorrow - name the app/file/task\n\n## Patterns\n- Apps I used most, topics that came up repeatedly\n\nOnly report what you can verify from the data. End with: \"**Next step:** [most important thing to continue]\"\n"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -X POST [URL_WITH_CREDENTIALS] ~ $ sqlite3 ~/.screenpipe/db.sqlite "SELECT pipe_name, status, started_at, finished_at, stdout, stderrFROM pipe_executionsORDER BY started_at DESCLIMIT 5;"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $...
|
NULL
|
|
63544
|
1379
|
23
|
2026-04-21T09:15:03.266299+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776762903266_m1.jpg...
|
PhpStorm
|
History for Selection
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Previous Difference
Next Difference
Jump to Source Previous Difference
Next Difference
Jump to Source
Side-by-side viewer
Do not ignore
Highlight words
Collapse Unchanged Fragments
Synchronize Scrolling
Settings
Help
1 difference
Revision 952cb5cd211942e772c1c1a7cb40135789aa3481
Revision 952cb5cd211942e772c1c1a7cb40135789aa3481
text/html
text/html
text/html
<?php
namespace Jiminny\Events\Activities\Crm;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Jiminny\Models\Activity;
use Jiminny\Models\Lead;
class LeadConverted
{
use InteractsWithSockets, SerializesModels;
/**
* The activity instance.
*
* @var Activity
*/
public $activity;
/**
* The converted Lead.
*
* @var Lead
*/
public $convertedLead;
/**
* Create a new event instance.
*
* @param Activity $activity
*/
public function __construct(Activity $activity, Lead $convertedLead)
{
$this->activity = $activity;
$this->convertedLead = $convertedLead;
}
}
Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea
Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea
text/html
text/html
text/html
<?php
namespace Jiminny\Events\Activities\Crm;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Jiminny\Models\Activity;
use Jiminny\Models\Lead;
class LeadConverted
{
use InteractsWithSockets;
use SerializesModels;
/**
* The activity instance.
*
* @var Activity
*/
public $activity;
/**
* The converted Lead.
*
* @var Lead
*/
public $convertedLead;
/**
* Create a new event instance.
*
* @param Activity $activity
*/
public function __construct(Activity $activity, Lead $convertedLead)
{
$this->activity = $activity;
$this->convertedLead = $convertedLead;
}
}
27a70bd6
27a70bd6
27.01.26, 18:09
27.01.26, 18:09
Lukas Kovalik
Lukas Kovalik
JY-20026 support both activity lead converted and lead converted
JY-20026 support both activity lead converted and lead converted
0a8f009f
0a8f009f
8.10.24, 13:23
8.10.24, 13:23
Vasil Vasilev
Vasil Vasilev
JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.
JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.
7e0be7bb
7e0be7bb
8.10.24, 11:36
8.10.24, 11:36
Vasil Vasilev
Vasil Vasilev
JY-14156 | Add typehints for LeadConverted event.
JY-14156 | Add typehints for LeadConverted event.
ea606076
ea606076
8.12.23, 11:57
8.12.23, 11:57
Shift
Shift
Remove redundant typing from DocBlocks
Remove redundant typing from DocBlocks
8a3996c1
8a3996c1
23.09.22, 13:50
23.09.22, 13:50
Rusi Papazov
Rusi Papazov
Run `php-cs-fixer fix app/Events`
Run `php-cs-fixer fix app/Events`
952cb5cd
952cb5cd
13.05.19, 18:09
13.05.19, 18:09
iva
iva
JMNY-3424 update recurring event activities with correct data
JMNY-3424 update recurring event activities with correct data
Version
Date
Author
Commit Message
Changes only
Commit Message:
Run `php-cs-fixer fix app/Events`
text/html
History for Selection...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Previous Difference","depth":2,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Difference","depth":2,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Jump to Source","depth":2,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Side-by-side viewer","depth":2,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Do not ignore","depth":2,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Highlight words","depth":2,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse Unchanged Fragments","depth":2,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Synchronize Scrolling","depth":2,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Settings","depth":2,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Help","depth":2,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1 difference","depth":1,"role_description":"text"},{"role":"AXStaticText","text":"Revision 952cb5cd211942e772c1c1a7cb40135789aa3481","depth":1,"role_description":"text"},{"role":"AXTextField","text":"Revision 952cb5cd211942e772c1c1a7cb40135789aa3481","depth":2,"value":"Revision 952cb5cd211942e772c1c1a7cb40135789aa3481","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Events\\Activities\\Crm;\n\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Lead;\n\nclass LeadConverted\n{\n use InteractsWithSockets, SerializesModels;\n\n /**\n * The activity instance.\n *\n * @var Activity\n */\n public $activity;\n\n /**\n * The converted Lead.\n *\n * @var Lead\n */\n public $convertedLead;\n\n /**\n * Create a new event instance.\n *\n * @param Activity $activity\n */\n public function __construct(Activity $activity, Lead $convertedLead)\n {\n $this->activity = $activity;\n $this->convertedLead = $convertedLead;\n }\n}","depth":2,"value":"<?php\n\nnamespace Jiminny\\Events\\Activities\\Crm;\n\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Lead;\n\nclass LeadConverted\n{\n use InteractsWithSockets, SerializesModels;\n\n /**\n * The activity instance.\n *\n * @var Activity\n */\n public $activity;\n\n /**\n * The converted Lead.\n *\n * @var Lead\n */\n public $convertedLead;\n\n /**\n * Create a new event instance.\n *\n * @param Activity $activity\n */\n public function __construct(Activity $activity, Lead $convertedLead)\n {\n $this->activity = $activity;\n $this->convertedLead = $convertedLead;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea","depth":1,"role_description":"text"},{"role":"AXTextField","text":"Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea","depth":2,"value":"Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Events\\Activities\\Crm;\n\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Lead;\n\nclass LeadConverted\n{\n use InteractsWithSockets;\n use SerializesModels;\n\n /**\n * The activity instance.\n *\n * @var Activity\n */\n public $activity;\n\n /**\n * The converted Lead.\n *\n * @var Lead\n */\n public $convertedLead;\n\n /**\n * Create a new event instance.\n *\n * @param Activity $activity\n */\n public function __construct(Activity $activity, Lead $convertedLead)\n {\n $this->activity = $activity;\n $this->convertedLead = $convertedLead;\n }\n}","depth":2,"value":"<?php\n\nnamespace Jiminny\\Events\\Activities\\Crm;\n\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Lead;\n\nclass LeadConverted\n{\n use InteractsWithSockets;\n use SerializesModels;\n\n /**\n * The activity instance.\n *\n * @var Activity\n */\n public $activity;\n\n /**\n * The converted Lead.\n *\n * @var Lead\n */\n public $convertedLead;\n\n /**\n * Create a new event instance.\n *\n * @param Activity $activity\n */\n public function __construct(Activity $activity, Lead $convertedLead)\n {\n $this->activity = $activity;\n $this->convertedLead = $convertedLead;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"27a70bd6","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"27a70bd6","depth":5,"role_description":"text"},{"role":"AXCell","text":"27.01.26, 18:09","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"27.01.26, 18:09","depth":5,"role_description":"text"},{"role":"AXCell","text":"Lukas Kovalik","depth":4,"help_text":"Lukas Kovalik <kovaliklukas@gmail.com>","role_description":"cell"},{"role":"AXStaticText","text":"Lukas Kovalik","depth":5,"help_text":"Lukas Kovalik <kovaliklukas@gmail.com>","role_description":"text"},{"role":"AXCell","text":"JY-20026 support both activity lead converted and lead converted","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"JY-20026 support both activity lead converted and lead converted","depth":5,"role_description":"text"},{"role":"AXCell","text":"0a8f009f","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"0a8f009f","depth":5,"role_description":"text"},{"role":"AXCell","text":"8.10.24, 13:23","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"8.10.24, 13:23","depth":5,"role_description":"text"},{"role":"AXCell","text":"Vasil Vasilev","depth":4,"help_text":"Vasil Vasilev <vasil.vasilev@jiminny.com>","role_description":"cell"},{"role":"AXStaticText","text":"Vasil Vasilev","depth":5,"help_text":"Vasil Vasilev <vasil.vasilev@jiminny.com>","role_description":"text"},{"role":"AXCell","text":"JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.","depth":5,"role_description":"text"},{"role":"AXCell","text":"7e0be7bb","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"7e0be7bb","depth":5,"role_description":"text"},{"role":"AXCell","text":"8.10.24, 11:36","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"8.10.24, 11:36","depth":5,"role_description":"text"},{"role":"AXCell","text":"Vasil Vasilev","depth":4,"help_text":"Vasil Vasilev <vasil.vasilev@jiminny.com>","role_description":"cell"},{"role":"AXStaticText","text":"Vasil Vasilev","depth":5,"help_text":"Vasil Vasilev <vasil.vasilev@jiminny.com>","role_description":"text"},{"role":"AXCell","text":"JY-14156 | Add typehints for LeadConverted event.","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"JY-14156 | Add typehints for LeadConverted event.","depth":5,"role_description":"text"},{"role":"AXCell","text":"ea606076","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"ea606076","depth":5,"role_description":"text"},{"role":"AXCell","text":"8.12.23, 11:57","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"8.12.23, 11:57","depth":5,"role_description":"text"},{"role":"AXCell","text":"Shift","depth":4,"help_text":"Shift <shift@laravelshift.com>","role_description":"cell"},{"role":"AXStaticText","text":"Shift","depth":5,"help_text":"Shift <shift@laravelshift.com>","role_description":"text"},{"role":"AXCell","text":"Remove redundant typing from DocBlocks","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"Remove redundant typing from DocBlocks","depth":5,"role_description":"text"},{"role":"AXCell","text":"8a3996c1","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"8a3996c1","depth":5,"role_description":"text"},{"role":"AXCell","text":"23.09.22, 13:50","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"23.09.22, 13:50","depth":5,"role_description":"text"},{"role":"AXCell","text":"Rusi Papazov","depth":4,"help_text":"Rusi Papazov <rusi.papazov@jiminny.com>","role_description":"cell"},{"role":"AXStaticText","text":"Rusi Papazov","depth":5,"help_text":"Rusi Papazov <rusi.papazov@jiminny.com>","role_description":"text"},{"role":"AXCell","text":"Run `php-cs-fixer fix app/Events`","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"Run `php-cs-fixer fix app/Events`","depth":5,"role_description":"text"},{"role":"AXCell","text":"952cb5cd","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"952cb5cd","depth":5,"role_description":"text"},{"role":"AXCell","text":"13.05.19, 18:09","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"13.05.19, 18:09","depth":5,"role_description":"text"},{"role":"AXCell","text":"iva","depth":4,"help_text":"iva <ivetoo89@gmail.com>","role_description":"cell"},{"role":"AXStaticText","text":"iva","depth":5,"help_text":"iva <ivetoo89@gmail.com>","role_description":"text"},{"role":"AXCell","text":"JMNY-3424 update recurring event activities with correct data","depth":4,"role_description":"cell"},{"role":"AXStaticText","text":"JMNY-3424 update recurring event activities with correct data","depth":5,"role_description":"text"},{"role":"AXStaticText","text":"Version","depth":2,"role_description":"text"},{"role":"AXStaticText","text":"Date","depth":2,"role_description":"text"},{"role":"AXStaticText","text":"Author","depth":2,"role_description":"text"},{"role":"AXStaticText","text":"Commit Message","depth":2,"role_description":"text"},{"role":"AXCheckBox","text":"Changes only","depth":1,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Commit Message:","depth":1,"role_description":"text"},{"role":"AXTextField","text":"Run `php-cs-fixer fix app/Events`","depth":2,"value":"Run `php-cs-fixer fix app/Events`","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"History for Selection","depth":1,"role_description":"text"}]...
|
-1302620329703951492
|
7913272381269171667
|
click
|
accessibility
|
NULL
|
Previous Difference
Next Difference
Jump to Source Previous Difference
Next Difference
Jump to Source
Side-by-side viewer
Do not ignore
Highlight words
Collapse Unchanged Fragments
Synchronize Scrolling
Settings
Help
1 difference
Revision 952cb5cd211942e772c1c1a7cb40135789aa3481
Revision 952cb5cd211942e772c1c1a7cb40135789aa3481
text/html
text/html
text/html
<?php
namespace Jiminny\Events\Activities\Crm;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Jiminny\Models\Activity;
use Jiminny\Models\Lead;
class LeadConverted
{
use InteractsWithSockets, SerializesModels;
/**
* The activity instance.
*
* @var Activity
*/
public $activity;
/**
* The converted Lead.
*
* @var Lead
*/
public $convertedLead;
/**
* Create a new event instance.
*
* @param Activity $activity
*/
public function __construct(Activity $activity, Lead $convertedLead)
{
$this->activity = $activity;
$this->convertedLead = $convertedLead;
}
}
Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea
Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea
text/html
text/html
text/html
<?php
namespace Jiminny\Events\Activities\Crm;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Jiminny\Models\Activity;
use Jiminny\Models\Lead;
class LeadConverted
{
use InteractsWithSockets;
use SerializesModels;
/**
* The activity instance.
*
* @var Activity
*/
public $activity;
/**
* The converted Lead.
*
* @var Lead
*/
public $convertedLead;
/**
* Create a new event instance.
*
* @param Activity $activity
*/
public function __construct(Activity $activity, Lead $convertedLead)
{
$this->activity = $activity;
$this->convertedLead = $convertedLead;
}
}
27a70bd6
27a70bd6
27.01.26, 18:09
27.01.26, 18:09
Lukas Kovalik
Lukas Kovalik
JY-20026 support both activity lead converted and lead converted
JY-20026 support both activity lead converted and lead converted
0a8f009f
0a8f009f
8.10.24, 13:23
8.10.24, 13:23
Vasil Vasilev
Vasil Vasilev
JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.
JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.
7e0be7bb
7e0be7bb
8.10.24, 11:36
8.10.24, 11:36
Vasil Vasilev
Vasil Vasilev
JY-14156 | Add typehints for LeadConverted event.
JY-14156 | Add typehints for LeadConverted event.
ea606076
ea606076
8.12.23, 11:57
8.12.23, 11:57
Shift
Shift
Remove redundant typing from DocBlocks
Remove redundant typing from DocBlocks
8a3996c1
8a3996c1
23.09.22, 13:50
23.09.22, 13:50
Rusi Papazov
Rusi Papazov
Run `php-cs-fixer fix app/Events`
Run `php-cs-fixer fix app/Events`
952cb5cd
952cb5cd
13.05.19, 18:09
13.05.19, 18:09
iva
iva
JMNY-3424 update recurring event activities with correct data
JMNY-3424 update recurring event activities with correct data
Version
Date
Author
Commit Message
Changes only
Commit Message:
Run `php-cs-fixer fix app/Events`
text/html
History for Selection...
|
NULL
|
|
63545
|
1380
|
31
|
2026-04-21T09:15:03.336291+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776762903336_m2.jpg...
|
PhpStorm
|
History for Selection
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Previous Difference
Next Difference
Jump to Source Previous Difference
Next Difference
Jump to Source
Side-by-side viewer
Do not ignore
Highlight words
Collapse Unchanged Fragments
Synchronize Scrolling
Settings
Help
1 difference
Revision 952cb5cd211942e772c1c1a7cb40135789aa3481
Revision 952cb5cd211942e772c1c1a7cb40135789aa3481
text/html
text/html
text/html
<?php
namespace Jiminny\Events\Activities\Crm;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Jiminny\Models\Activity;
use Jiminny\Models\Lead;
class LeadConverted
{
use InteractsWithSockets, SerializesModels;
/**
* The activity instance.
*
* @var Activity
*/
public $activity;
/**
* The converted Lead.
*
* @var Lead
*/
public $convertedLead;
/**
* Create a new event instance.
*
* @param Activity $activity
*/
public function __construct(Activity $activity, Lead $convertedLead)
{
$this->activity = $activity;
$this->convertedLead = $convertedLead;
}
}
Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea
Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea
text/html
text/html
text/html
<?php
namespace Jiminny\Events\Activities\Crm;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Jiminny\Models\Activity;
use Jiminny\Models\Lead;
class LeadConverted
{
use InteractsWithSockets;
use SerializesModels;
/**
* The activity instance.
*
* @var Activity
*/
public $activity;
/**
* The converted Lead.
*
* @var Lead
*/
public $convertedLead;
/**
* Create a new event instance.
*
* @param Activity $activity
*/
public function __construct(Activity $activity, Lead $convertedLead)
{
$this->activity = $activity;
$this->convertedLead = $convertedLead;
}
}
27a70bd6
27a70bd6
27.01.26, 18:09
27.01.26, 18:09
Lukas Kovalik
Lukas Kovalik
JY-20026 support both activity lead converted and lead converted
JY-20026 support both activity lead converted and lead converted
0a8f009f
0a8f009f
8.10.24, 13:23
8.10.24, 13:23
Vasil Vasilev
Vasil Vasilev
JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.
JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.
7e0be7bb
7e0be7bb
8.10.24, 11:36
8.10.24, 11:36
Vasil Vasilev
Vasil Vasilev
JY-14156 | Add typehints for LeadConverted event.
JY-14156 | Add typehints for LeadConverted event.
ea606076
ea606076
8.12.23, 11:57
8.12.23, 11:57
Shift
Shift
Remove redundant typing from DocBlocks
Remove redundant typing from DocBlocks
8a3996c1
8a3996c1
23.09.22, 13:50
23.09.22, 13:50
Rusi Papazov
Rusi Papazov
Run `php-cs-fixer fix app/Events`
Run `php-cs-fixer fix app/Events`
952cb5cd
952cb5cd
13.05.19, 18:09
13.05.19, 18:09
iva
iva
JMNY-3424 update recurring event activities with correct data
JMNY-3424 update recurring event activities with correct data
Version
Date
Author
Commit Message
Changes only
Commit Message:
Run `php-cs-fixer fix app/Events`
text/html
History for Selection...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Previous Difference","depth":2,"bounds":{"left":0.12799202,"top":0.044692736,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Difference","depth":2,"bounds":{"left":0.13663563,"top":0.044692736,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Jump to Source","depth":2,"bounds":{"left":0.14527926,"top":0.044692736,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Side-by-side viewer","depth":2,"bounds":{"left":0.15724733,"top":0.044692736,"width":0.04720745,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Do not ignore","depth":2,"bounds":{"left":0.2087766,"top":0.044692736,"width":0.03557181,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Highlight words","depth":2,"bounds":{"left":0.24634309,"top":0.044692736,"width":0.03956117,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse Unchanged Fragments","depth":2,"bounds":{"left":0.2869016,"top":0.044692736,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Synchronize Scrolling","depth":2,"bounds":{"left":0.29554522,"top":0.044692736,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Settings","depth":2,"bounds":{"left":0.30418882,"top":0.044692736,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Help","depth":2,"bounds":{"left":0.3151596,"top":0.044692736,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1 difference","depth":1,"bounds":{"left":0.9744016,"top":0.04309657,"width":0.024268618,"height":0.022346368},"role_description":"text"},{"role":"AXStaticText","text":"Revision 952cb5cd211942e772c1c1a7cb40135789aa3481","depth":1,"bounds":{"left":0.13331117,"top":0.065442935,"width":0.42486703,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"Revision 952cb5cd211942e772c1c1a7cb40135789aa3481","depth":2,"bounds":{"left":0.13331117,"top":0.065442935,"width":0.42486703,"height":0.013567438},"value":"Revision 952cb5cd211942e772c1c1a7cb40135789aa3481","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"bounds":{"left":0.13331117,"top":0.065442935,"width":0.118351065,"height":0.013567438},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Events\\Activities\\Crm;\n\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Lead;\n\nclass LeadConverted\n{\n use InteractsWithSockets, SerializesModels;\n\n /**\n * The activity instance.\n *\n * @var Activity\n */\n public $activity;\n\n /**\n * The converted Lead.\n *\n * @var Lead\n */\n public $convertedLead;\n\n /**\n * Create a new event instance.\n *\n * @param Activity $activity\n */\n public function __construct(Activity $activity, Lead $convertedLead)\n {\n $this->activity = $activity;\n $this->convertedLead = $convertedLead;\n }\n}","depth":2,"bounds":{"left":0.12666224,"top":0.08060654,"width":0.42021278,"height":0.48603353},"value":"<?php\n\nnamespace Jiminny\\Events\\Activities\\Crm;\n\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Lead;\n\nclass LeadConverted\n{\n use InteractsWithSockets, SerializesModels;\n\n /**\n * The activity instance.\n *\n * @var Activity\n */\n public $activity;\n\n /**\n * The converted Lead.\n *\n * @var Lead\n */\n public $convertedLead;\n\n /**\n * Create a new event instance.\n *\n * @param Activity $activity\n */\n public function __construct(Activity $activity, Lead $convertedLead)\n {\n $this->activity = $activity;\n $this->convertedLead = $convertedLead;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea","depth":1,"bounds":{"left":0.57413566,"top":0.065442935,"width":0.4245346,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea","depth":2,"bounds":{"left":0.57413566,"top":0.065442935,"width":0.4245346,"height":0.013567438},"value":"Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"bounds":{"left":0.57413566,"top":0.065442935,"width":0.11934841,"height":0.013567438},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Events\\Activities\\Crm;\n\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Lead;\n\nclass LeadConverted\n{\n use InteractsWithSockets;\n use SerializesModels;\n\n /**\n * The activity instance.\n *\n * @var Activity\n */\n public $activity;\n\n /**\n * The converted Lead.\n *\n * @var Lead\n */\n public $convertedLead;\n\n /**\n * Create a new event instance.\n *\n * @param Activity $activity\n */\n public function __construct(Activity $activity, Lead $convertedLead)\n {\n $this->activity = $activity;\n $this->convertedLead = $convertedLead;\n }\n}","depth":2,"bounds":{"left":0.58011967,"top":0.08060654,"width":0.41988033,"height":0.48603353},"value":"<?php\n\nnamespace Jiminny\\Events\\Activities\\Crm;\n\nuse Illuminate\\Queue\\SerializesModels;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Lead;\n\nclass LeadConverted\n{\n use InteractsWithSockets;\n use SerializesModels;\n\n /**\n * The activity instance.\n *\n * @var Activity\n */\n public $activity;\n\n /**\n * The converted Lead.\n *\n * @var Lead\n */\n public $convertedLead;\n\n /**\n * Create a new event instance.\n *\n * @param Activity $activity\n */\n public function __construct(Activity $activity, Lead $convertedLead)\n {\n $this->activity = $activity;\n $this->convertedLead = $convertedLead;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"27a70bd6","depth":4,"bounds":{"left":0.13231383,"top":0.61532325,"width":0.14727394,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"27a70bd6","depth":5,"bounds":{"left":0.13231383,"top":0.61532325,"width":0.14727394,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"27.01.26, 18:09","depth":4,"bounds":{"left":0.27992022,"top":0.61532325,"width":0.140625,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"27.01.26, 18:09","depth":5,"bounds":{"left":0.27992022,"top":0.61532325,"width":0.140625,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"Lukas Kovalik","depth":4,"bounds":{"left":0.42087767,"top":0.61532325,"width":0.16256648,"height":0.01915403},"help_text":"Lukas Kovalik <kovaliklukas@gmail.com>","role_description":"cell"},{"role":"AXStaticText","text":"Lukas Kovalik","depth":5,"bounds":{"left":0.42087767,"top":0.61532325,"width":0.16256648,"height":0.01915403},"help_text":"Lukas Kovalik <kovaliklukas@gmail.com>","role_description":"text"},{"role":"AXCell","text":"JY-20026 support both activity lead converted and lead converted","depth":4,"bounds":{"left":0.5837766,"top":0.61532325,"width":0.41023937,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"JY-20026 support both activity lead converted and lead converted","depth":5,"bounds":{"left":0.5837766,"top":0.61532325,"width":0.41023937,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"0a8f009f","depth":4,"bounds":{"left":0.13231383,"top":0.63527536,"width":0.14727394,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"0a8f009f","depth":5,"bounds":{"left":0.13231383,"top":0.63527536,"width":0.14727394,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"8.10.24, 13:23","depth":4,"bounds":{"left":0.27992022,"top":0.63527536,"width":0.140625,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"8.10.24, 13:23","depth":5,"bounds":{"left":0.27992022,"top":0.63527536,"width":0.140625,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"Vasil Vasilev","depth":4,"bounds":{"left":0.42087767,"top":0.63527536,"width":0.16256648,"height":0.01915403},"help_text":"Vasil Vasilev <vasil.vasilev@jiminny.com>","role_description":"cell"},{"role":"AXStaticText","text":"Vasil Vasilev","depth":5,"bounds":{"left":0.42087767,"top":0.63527536,"width":0.16256648,"height":0.01915403},"help_text":"Vasil Vasilev <vasil.vasilev@jiminny.com>","role_description":"text"},{"role":"AXCell","text":"JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.","depth":4,"bounds":{"left":0.5837766,"top":0.63527536,"width":0.41023937,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.","depth":5,"bounds":{"left":0.5837766,"top":0.63527536,"width":0.41023937,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"7e0be7bb","depth":4,"bounds":{"left":0.13231383,"top":0.6552275,"width":0.14727394,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"7e0be7bb","depth":5,"bounds":{"left":0.13231383,"top":0.6552275,"width":0.14727394,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"8.10.24, 11:36","depth":4,"bounds":{"left":0.27992022,"top":0.6552275,"width":0.140625,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"8.10.24, 11:36","depth":5,"bounds":{"left":0.27992022,"top":0.6552275,"width":0.140625,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"Vasil Vasilev","depth":4,"bounds":{"left":0.42087767,"top":0.6552275,"width":0.16256648,"height":0.01915403},"help_text":"Vasil Vasilev <vasil.vasilev@jiminny.com>","role_description":"cell"},{"role":"AXStaticText","text":"Vasil Vasilev","depth":5,"bounds":{"left":0.42087767,"top":0.6552275,"width":0.16256648,"height":0.01915403},"help_text":"Vasil Vasilev <vasil.vasilev@jiminny.com>","role_description":"text"},{"role":"AXCell","text":"JY-14156 | Add typehints for LeadConverted event.","depth":4,"bounds":{"left":0.5837766,"top":0.6552275,"width":0.41023937,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"JY-14156 | Add typehints for LeadConverted event.","depth":5,"bounds":{"left":0.5837766,"top":0.6552275,"width":0.41023937,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"ea606076","depth":4,"bounds":{"left":0.13231383,"top":0.67517954,"width":0.14727394,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"ea606076","depth":5,"bounds":{"left":0.13231383,"top":0.67517954,"width":0.14727394,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"8.12.23, 11:57","depth":4,"bounds":{"left":0.27992022,"top":0.67517954,"width":0.140625,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"8.12.23, 11:57","depth":5,"bounds":{"left":0.27992022,"top":0.67517954,"width":0.140625,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"Shift","depth":4,"bounds":{"left":0.42087767,"top":0.67517954,"width":0.16256648,"height":0.01915403},"help_text":"Shift <shift@laravelshift.com>","role_description":"cell"},{"role":"AXStaticText","text":"Shift","depth":5,"bounds":{"left":0.42087767,"top":0.67517954,"width":0.16256648,"height":0.01915403},"help_text":"Shift <shift@laravelshift.com>","role_description":"text"},{"role":"AXCell","text":"Remove redundant typing from DocBlocks","depth":4,"bounds":{"left":0.5837766,"top":0.67517954,"width":0.41023937,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"Remove redundant typing from DocBlocks","depth":5,"bounds":{"left":0.5837766,"top":0.67517954,"width":0.41023937,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"8a3996c1","depth":4,"bounds":{"left":0.13231383,"top":0.69513166,"width":0.14727394,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"8a3996c1","depth":5,"bounds":{"left":0.13231383,"top":0.69513166,"width":0.14727394,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"23.09.22, 13:50","depth":4,"bounds":{"left":0.27992022,"top":0.69513166,"width":0.140625,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"23.09.22, 13:50","depth":5,"bounds":{"left":0.27992022,"top":0.69513166,"width":0.140625,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"Rusi Papazov","depth":4,"bounds":{"left":0.42087767,"top":0.69513166,"width":0.16256648,"height":0.01915403},"help_text":"Rusi Papazov <rusi.papazov@jiminny.com>","role_description":"cell"},{"role":"AXStaticText","text":"Rusi Papazov","depth":5,"bounds":{"left":0.42087767,"top":0.69513166,"width":0.16256648,"height":0.01915403},"help_text":"Rusi Papazov <rusi.papazov@jiminny.com>","role_description":"text"},{"role":"AXCell","text":"Run `php-cs-fixer fix app/Events`","depth":4,"bounds":{"left":0.5837766,"top":0.69513166,"width":0.41023937,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"Run `php-cs-fixer fix app/Events`","depth":5,"bounds":{"left":0.5837766,"top":0.69513166,"width":0.41023937,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"952cb5cd","depth":4,"bounds":{"left":0.13231383,"top":0.7150838,"width":0.14727394,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"952cb5cd","depth":5,"bounds":{"left":0.13231383,"top":0.7150838,"width":0.14727394,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"13.05.19, 18:09","depth":4,"bounds":{"left":0.27992022,"top":0.7150838,"width":0.140625,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"13.05.19, 18:09","depth":5,"bounds":{"left":0.27992022,"top":0.7150838,"width":0.140625,"height":0.01915403},"role_description":"text"},{"role":"AXCell","text":"iva","depth":4,"bounds":{"left":0.42087767,"top":0.7150838,"width":0.16256648,"height":0.01915403},"help_text":"iva <ivetoo89@gmail.com>","role_description":"cell"},{"role":"AXStaticText","text":"iva","depth":5,"bounds":{"left":0.42087767,"top":0.7150838,"width":0.16256648,"height":0.01915403},"help_text":"iva <ivetoo89@gmail.com>","role_description":"text"},{"role":"AXCell","text":"JMNY-3424 update recurring event activities with correct data","depth":4,"bounds":{"left":0.5837766,"top":0.7150838,"width":0.41023937,"height":0.01915403},"role_description":"cell"},{"role":"AXStaticText","text":"JMNY-3424 update recurring event activities with correct data","depth":5,"bounds":{"left":0.5837766,"top":0.7150838,"width":0.41023937,"height":0.01915403},"role_description":"text"},{"role":"AXStaticText","text":"Version","depth":2,"role_description":"text"},{"role":"AXStaticText","text":"Date","depth":2,"role_description":"text"},{"role":"AXStaticText","text":"Author","depth":2,"role_description":"text"},{"role":"AXStaticText","text":"Commit Message","depth":2,"role_description":"text"},{"role":"AXCheckBox","text":"Changes only","depth":1,"bounds":{"left":0.13198139,"top":0.57222664,"width":0.03723404,"height":0.022346368},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Commit Message:","depth":1,"bounds":{"left":0.13198139,"top":0.915403,"width":0.86269945,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"Run `php-cs-fixer fix app/Events`","depth":2,"bounds":{"left":0.13231383,"top":0.93296087,"width":0.86203456,"height":0.05347167},"value":"Run `php-cs-fixer fix app/Events`","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"bounds":{"left":0.13231383,"top":0.93296087,"width":0.86203456,"height":0.05347167},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"History for Selection","depth":1,"bounds":{"left":0.54022604,"top":0.023942538,"width":0.045877658,"height":0.012769354},"role_description":"text"}]...
|
-1302620329703951492
|
7913272381269171667
|
click
|
accessibility
|
NULL
|
Previous Difference
Next Difference
Jump to Source Previous Difference
Next Difference
Jump to Source
Side-by-side viewer
Do not ignore
Highlight words
Collapse Unchanged Fragments
Synchronize Scrolling
Settings
Help
1 difference
Revision 952cb5cd211942e772c1c1a7cb40135789aa3481
Revision 952cb5cd211942e772c1c1a7cb40135789aa3481
text/html
text/html
text/html
<?php
namespace Jiminny\Events\Activities\Crm;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Jiminny\Models\Activity;
use Jiminny\Models\Lead;
class LeadConverted
{
use InteractsWithSockets, SerializesModels;
/**
* The activity instance.
*
* @var Activity
*/
public $activity;
/**
* The converted Lead.
*
* @var Lead
*/
public $convertedLead;
/**
* Create a new event instance.
*
* @param Activity $activity
*/
public function __construct(Activity $activity, Lead $convertedLead)
{
$this->activity = $activity;
$this->convertedLead = $convertedLead;
}
}
Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea
Revision 8a3996c134246ad84b8c62dfa7befe34022bf7ea
text/html
text/html
text/html
<?php
namespace Jiminny\Events\Activities\Crm;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\InteractsWithSockets;
use Jiminny\Models\Activity;
use Jiminny\Models\Lead;
class LeadConverted
{
use InteractsWithSockets;
use SerializesModels;
/**
* The activity instance.
*
* @var Activity
*/
public $activity;
/**
* The converted Lead.
*
* @var Lead
*/
public $convertedLead;
/**
* Create a new event instance.
*
* @param Activity $activity
*/
public function __construct(Activity $activity, Lead $convertedLead)
{
$this->activity = $activity;
$this->convertedLead = $convertedLead;
}
}
27a70bd6
27a70bd6
27.01.26, 18:09
27.01.26, 18:09
Lukas Kovalik
Lukas Kovalik
JY-20026 support both activity lead converted and lead converted
JY-20026 support both activity lead converted and lead converted
0a8f009f
0a8f009f
8.10.24, 13:23
8.10.24, 13:23
Vasil Vasilev
Vasil Vasilev
JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.
JY-14156 | Add useful comments explaining that LeadConverted is a Salesforce specific event. ALso fixed whitespace issue.
7e0be7bb
7e0be7bb
8.10.24, 11:36
8.10.24, 11:36
Vasil Vasilev
Vasil Vasilev
JY-14156 | Add typehints for LeadConverted event.
JY-14156 | Add typehints for LeadConverted event.
ea606076
ea606076
8.12.23, 11:57
8.12.23, 11:57
Shift
Shift
Remove redundant typing from DocBlocks
Remove redundant typing from DocBlocks
8a3996c1
8a3996c1
23.09.22, 13:50
23.09.22, 13:50
Rusi Papazov
Rusi Papazov
Run `php-cs-fixer fix app/Events`
Run `php-cs-fixer fix app/Events`
952cb5cd
952cb5cd
13.05.19, 18:09
13.05.19, 18:09
iva
iva
JMNY-3424 update recurring event activities with correct data
JMNY-3424 update recurring event activities with correct data
Version
Date
Author
Commit Message
Changes only
Commit Message:
Run `php-cs-fixer fix app/Events`
text/html
History for Selection...
|
NULL
|
|
69503
|
1599
|
6
|
2026-04-22T08:13:17.958222+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776845597958_m2.jpg...
|
iTerm2
|
ec2-user@ip-10-30-159-186:~
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Properties:
📝 hubspot_owner_id: 2837 Properties:
📝 hubspot_owner_id: 2837 changes
📝 email: 4471 changes
📝 phone: 3661 changes
📝 lastname: 16150 changes
📝 firstname: 22622 changes
📝 associatedcompanyid: 4860 changes
📝 mobilephone: 13 changes
📝 jobtitle: 5 changes
📝 country: 1 changes
🔔 Event: creation
Count: 4471
🔔 Event: association_change
Count: 17648
📦 Object Type: deal
🔔 Event: creation
Count: 7588
🔔 Event: property_change
Count: 82720
Properties:
📝 dealname: 4204 changes
📝 amount: 22594 changes
📝 hs_deal_stage_probability: 17646 changes
📝 pipeline: 4356 changes
📝 dealstage: 14626 changes
📝 closedate: 11096 changes
📝 hubspot_owner_id: 6094 changes
📝 selected_date: 1969 changes
📝 deal_currency_code: 135 changes
🔔 Event: association_change
Count: 22773
INFO Looking for metrics: Config 878 (Dingus and Zazzy - 929), Date 2026-04-16.
📊 Webhook Metrics for Config 878 (Dingus and Zazzy - 929)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: property_change
Count: 158
Properties:
📝 amount_in_home_currency: 24 changes
📝 hs_deal_stage_probability: 35 changes
📝 hs_manual_forecast_category: 27 changes
📝 closedate: 23 changes
📝 lost_reason: 9 changes
📝 dealstage: 27 changes
📝 dealname: 9 changes
📝 hubspot_owner_id: 2 changes
📝 source_attribution: 2 changes
🔔 Event: creation
Count: 18
🔔 Event: association_change
Count: 52
📦 Object Type: company
🔔 Event: property_change
Count: 59
Properties:
📝 domain: 14 changes
📝 name: 14 changes
📝 hubspot_owner_id: 17 changes
📝 industry: 5 changes
📝 phone: 4 changes
📝 country: 5 changes
🔔 Event: association_change
Count: 69
🔔 Event: creation
Count: 14
📦 Object Type: contact
🔔 Event: property_change
Count: 192
Properties:
📝 phone: 8 changes
📝 lastname: 22 changes
📝 firstname: 22 changes
📝 jobtitle: 20 changes
📝 hubspot_owner_id: 76 changes
📝 email: 17 changes
📝 associatedcompanyid: 16 changes
📝 mobilephone: 11 changes
🔔 Event: association_change
Count: 53
🔔 Event: creation
Count: 20
INFO Looking for metrics: Config 671 (CosmosID - 691), Date 2026-04-16.
📊 Webhook Metrics for Config 671 (CosmosID - 691)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: creation
Count: 5
🔔 Event: property_change
Count: 15
Properties:
📝 hubspot_owner_id: 6 changes
📝 name: 5 changes
📝 domain: 3 changes
📝 phone: 1 changes
🔔 Event: association_change
Count: 44
📦 Object Type: deal
🔔 Event: property_change
Count: 78
Properties:
📝 hs_deal_stage_probability: 14 changes
📝 createdate: 10 changes
📝 hs_manual_forecast_category: 13 changes
📝 deal_currency_code: 4 changes
📝 amount: 20 changes
📝 closedate: 8 changes
📝 dealstage: 4 changes
📝 dealname: 3 changes
📝 dealtype: 1 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: association_change
Count: 30
🔔 Event: creation
Count: 10
📦 Object Type: contact
🔔 Event: creation
Count: 13
🔔 Event: property_change
Count: 145
Properties:
📝 jobtitle: 77 changes
📝 hubspot_owner_id: 12 changes
📝 firstname: 9 changes
📝 phone: 5 changes
📝 lastname: 8 changes
📝 email: 13 changes
📝 associatedcompanyid: 12 changes
📝 country: 9 changes
🔔 Event: association_change
Count: 34
INFO Looking for metrics: Config 652 (Abode - 673), Date 2026-04-16.
📊 Webhook Metrics for Config 652 (Abode - 673)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 1
Properties:
📝 country: 1 changes
🔔 Event: association_change
Count: 10
📦 Object Type: contact
🔔 Event: creation
Count: 6
🔔 Event: property_change
Count: 40
Properties:
📝 phone: 3 changes
📝 hubspot_owner_id: 8 changes
📝 email: 6 changes
📝 associatedcompanyid: 4 changes
📝 country: 4 changes
📝 lastname: 5 changes
📝 jobtitle: 4 changes
📝 firstname: 5 changes
📝 mobilephone: 1 changes
🔔 Event: association_change
Count: 9
📦 Object Type: deal
🔔 Event: creation
Count: 1
🔔 Event: association_change
Count: 3
🔔 Event: property_change
Count: 13
Properties:
📝 dealstage: 4 changes
📝 hs_deal_stage_probability: 5 changes
📝 dealname: 1 changes
📝 closedate: 1 changes
📝 hs_manual_forecast_category: 1 changes
📝 amount: 1 changes
INFO Looking for metrics: Config 1049 (Classavo - 851), Date 2026-04-16.
📊 Webhook Metrics for Config 1049 (Classavo - 851)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: association_change
Count: 2
📦 Object Type: contact
🔔 Event: association_change
Count: 1
🔔 Event: property_change
Count: 3
Properties:
📝 firstname: 1 changes
📝 lastname: 1 changes
📝 jobtitle: 1 changes
📦 Object Type: deal
🔔 Event: association_change
Count: 3
🔔 Event: property_change
Count: 14
Properties:
📝 dealstage: 4 changes
📝 hs_deal_stage_probability: 5 changes
📝 closedate: 3 changes
📝 deal_currency_code: 1 changes
📝 amount: 1 changes
🔔 Event: creation
Count: 1
INFO Looking for metrics: Config 290 (D1 Training - 308), Date 2026-04-16.
📊 Webhook Metrics for Config 290 (D1 Training - 308)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 5
Properties:
📝 name: 4 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: creation
Count: 1
🔔 Event: association_change
Count: 6
📦 Object Type: deal
🔔 Event: association_change
Count: 40
🔔 Event: creation
Count: 35
🔔 Event: property_change
Count: 405
Properties:
📝 hs_deal_stage_probability: 149 changes
📝 dealtype: 34 changes
📝 dealstage: 114 changes
📝 hubspot_owner_id: 5 changes
📝 closedate: 97 changes
📝 dealname: 3 changes
📝 pipeline: 2 changes
📝 hs_manual_forecast_category: 1 changes
📦 Object Type: contact
🔔 Event: creation
Count: 50
🔔 Event: property_change
Count: 314
Properties:
📝 lastname: 53 changes
📝 email: 46 changes
📝 firstname: 58 changes
📝 mobilephone: 26 changes
📝 phone: 70 changes
📝 hubspot_owner_id: 59 changes
📝 associatedcompanyid: 2 changes
🔔 Event: association_change
Count: 42
INFO Looking for metrics: Config 1019 (SimpleConsign - 1088), Date 2026-04-16.
📊 Webhook Metrics for Config 1019 (SimpleConsign - 1088)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 692
🔔 Event: property_change
Count: 2659
Properties:
📝 email: 334 changes
📝 hubspot_owner_id: 980 changes
📝 associatedcompanyid: 327 changes
📝 lastname: 332 changes
📝 phone: 20 changes
📝 firstname: 333 changes
📝 jobtitle: 321 changes
📝 mobilephone: 7 changes
📝 country: 5 changes
🔔 Event: creation
Count: 335
📦 Object Type: company
🔔 Event: association_change
Count: 698
🔔 Event: property_change
Count: 637
Properties:
📝 domain: 310 changes
📝 name: 312 changes
📝 phone: 5 changes
📝 hubspot_owner_id: 4 changes
📝 country: 3 changes
📝 industry: 3 changes
🔔 Event: creation
Count: 312
📦 Object Type: deal
🔔 Event: association_change
Count: 46
🔔 Event: creation
Count: 9
INFO Looking for metrics: Config 311 (Lemon.io - 329), Date 2026-04-16.
📊 Webhook Metrics for Config 311 (Lemon.io - 329)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 128
Properties:
📝 domain: 26 changes
📝 name: 24 changes
📝 industry: 18 changes
📝 country: 17 changes
📝 hubspot_owner_id: 32 changes
📝 phone: 11 changes
🔔 Event: creation
Count: 26
🔔 Event: association_change
Count: 120
📦 Object Type: deal
🔔 Event: creation
Count: 16
🔔 Event: property_change
Count: 385
Properties:
📝 closedate: 21 changes
📝 dealstage: 49 changes
📝 days_to_close: 19 changes
📝 hs_deal_stage_probability: 58 changes
📝 hs_manual_forecast_category: 34 changes
📝 hs_next_step: 14 changes
📝 level_of_involvement: 15 changes
📝 hubspot_owner_id: 20 changes
📝 freelancers_under_review: 31 changes
📝 pipeline: 8 changes
📝 dealname: 30 changes
📝 deal_currency_code: 8 changes
📝 amount: 13 changes
📝 hs_priority: 8 changes
📝 budget_limitations: 6 changes
📝 need: 7 changes
📝 positive_outcome: 6 changes
📝 competition: 6 changes
📝 negative_consequences: 6 changes
📝 selection_process: 8 changes
📝 project_team_structure: 7 changes
📝 champion: 6 changes
📝 red_flags: 5 changes
🔔 Event: association_change
Count: 63
📦 Object Type: contact
🔔 Event: property_change
Count: 336
Properties:
📝 hubspot_owner_id: 74 changes
📝 email: 56 changes
📝 firstname: 52 changes
📝 lastname: 48 changes
📝 country: 21 changes
📝 jobtitle: 26 changes
📝 associatedcompanyid: 39 changes
📝 phone: 15 changes
📝 mobilephone: 5 changes
🔔 Event: association_change
Count: 103
🔔 Event: creation
Count: 54
INFO Looking for metrics: Config 802 (Street Group - 853), Date 2026-04-16.
📊 Webhook Metrics for Config 802 (Street Group - 853)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: creation
Count: 252
🔔 Event: association_change
Count: 558
🔔 Event: property_change
Count: 1830
Properties:
📝 phone: 170 changes
📝 lastname: 306 changes
📝 firstname: 318 changes
📝 email: 297 changes
📝 country: 287 changes
📝 jobtitle: 208 changes
📝 associatedcompanyid: 180 changes
📝 hubspot_owner_id: 52 changes
📝 mobilephone: 12 changes
📦 Object Type: company
🔔 Event: association_change
Count: 617
🔔 Event: creation
Count: 31
🔔 Event: property_change
Count: 94
Properties:
📝 industry: 6 changes
📝 country: 10 changes
📝 phone: 10 changes
📝 name: 22 changes
📝 domain: 35 changes
📝 hubspot_owner_id: 11 changes
📦 Object Type: deal
🔔 Event: association_change
Count: 121
🔔 Event: property_change
Count: 338
Properties:
📝 hs_deal_stage_probability: 78 changes
📝 deal_currency_code: 22 changes
📝 amount: 36 changes
📝 closedate: 42 changes
📝 dealstage: 91 changes
📝 dealtype: 2 changes
📝 hs_manual_forecast_category: 28 changes
📝 dealname: 8 changes
📝 hs_forecast_probability: 16 changes
📝 invoice_start_date: 8 changes
📝 hubspot_owner_id: 7 changes
🔔 Event: creation
Count: 23
INFO Looking for metrics: Config 1053 (Sensi.AI - 1117), Date 2026-04-16.
📊 Webhook Metrics for Config 1053 (Sensi.AI - 1117)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 156
Properties:
📝 domain: 32 changes
📝 phone: 23 changes
📝 industry: 27 changes
📝 country: 29 changes
📝 name: 39 changes
📝 hubspot_owner_id: 6 changes
🔔 Event: creation
Count: 39
🔔 Event: association_change
Count: 1499
📦 Object Type: deal
🔔 Event: property_change
Count: 491
Properties:
📝 hs_deal_stage_probability: 96 changes
📝 hs_next_step: 46 changes
📝 next_steps_jiminny: 38 changes
📝 hubspot_owner_id: 17 changes
📝 monthly_billable_hours: 10 changes
📝 closedate: 18 changes
📝 amount: 119 changes
📝 deal_currency_code: 115 changes
📝 dealstage: 23 changes
📝 dealname: 4 changes
📝 client_segmentation_jiminny: 1 changes
📝 pain_points__main_objections: 1 changes
📝 product_feedback: 1 changes
📝 red_flags: 1 changes
📝 competitors: 1 changes
🔔 Event: association_change
Count: 98
🔔 Event: creation
Count: 74
📦 Object Type: contact
🔔 Event: association_change
Count: 1577
🔔 Event: property_change
Count: 3768
Properties:
📝 email: 857 changes
📝 mobilephone: 104 changes
📝 lastname: 854 changes
📝 jobtitle: 98 changes
📝 phone: 130 changes
📝 firstname: 870 changes
📝 hubspot_owner_id: 92 changes
📝 associatedcompanyid: 737 changes
📝 country: 26 changes
🔔 Event: creation
Count: 872
INFO Looking for metrics: Config 87 (Repsly - 93), Date 2026-04-16.
📊 Webhook Metrics for Config 87 (Repsly - 93)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 295
🔔 Event: property_change
Count: 1285
Properties:
📝 lastname: 150 changes
📝 email: 128 changes
📝 firstname: 162 changes
📝 associatedcompanyid: 145 changes
📝 country: 130 changes
📝 hubspot_owner_id: 175 changes
📝 mobilephone: 203 changes
📝 phone: 60 changes
📝 jobtitle: 132 changes
🔔 Event: creation
Count: 159
📦 Object Type: deal
🔔 Event: creation
Count: 3
🔔 Event: association_change
Count: 13
🔔 Event: property_change
Count: 23
Properties:
📝 hs_deal_stage_probability: 4 changes
📝 amount: 14 changes
📝 hubspot_owner_id: 1 changes
📝 closedate: 2 changes
📝 hs_next_step: 1 changes
📝 dealstage: 1 changes
📦 Object Type: company
🔔 Event: association_change
Count: 298
🔔 Event: property_change
Count: 23
Properties:
📝 domain: 9 changes
📝 country: 4 changes
📝 name: 6 changes
📝 hubspot_owner_id: 3 changes
📝 phone: 1 changes
🔔 Event: creation
Count: 9
INFO Looking for metrics: Config 518 (Prolific - 544), Date 2026-04-16.
📊 Webhook Metrics for Config 518 (Prolific - 544)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: property_change
Count: 14
Properties:
📝 amount: 3 changes
📝 hs_deal_stage_probability: 4 changes
📝 dealname: 3 changes
📝 closedate: 3 changes
📝 dealstage: 1 changes
🔔 Event: creation
Count: 3
🔔 Event: association_change
Count: 9
📦 Object Type: contact
🔔 Event: creation
Count: 534
🔔 Event: property_change
Count: 7533
Properties:
📝 jobtitle: 73 changes
📝 phone: 2 changes
📝 email: 561 changes
📝 lastname: 81 changes
📝 firstname: 90 changes
📝 associatedcompanyid: 166 changes
📝 hubspot_owner_id: 6151 changes
📝 country: 409 changes
🔔 Event: association_change
Count: 349
📦 Object Type: company
🔔 Event: creation
Count: 30
🔔 Event: association_change
Count: 352
🔔 Event: property_change
Count: 575
Properties:
📝 domain: 30 changes
📝 country: 421 changes
📝 phone: 12 changes
📝 industry: 18 changes
📝 name: 23 changes
📝 hubspot_owner_id: 71 changes
INFO Looking for metrics: Config 761 (Ressio Software - 770), Date 2026-04-16.
📊 Webhook Metrics for Config 761 (Ressio Software - 770)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 1370
Properties:
📝 hubspot_owner_id: 471 changes
📝 domain: 199 changes
📝 name: 202 changes
📝 country: 163 changes
📝 industry: 170 changes
📝 phone: 165 changes
🔔 Event: creation
Count: 207
🔔 Event: association_change
Count: 435
📦 Object Type: contact
🔔 Event: property_change
Count: 1582
Properties:
📝 hubspot_owner_id: 476 changes
📝 phone: 155 changes
📝 mobilephone: 97 changes
📝 lastname: 153 changes
📝 email: 109 changes
📝 associatedcompanyid: 160 changes
📝 firstname: 163 changes
📝 jobtitle: 139 changes
📝 country: 130 changes
🔔 Event: creation
Count: 152
🔔 Event: association_change
Count: 386
📦 Object Type: deal
🔔 Event: property_change
Count: 119
Properties:
📝 closedate: 16 changes
📝 dealstage: 36 changes
📝 hs_deal_stage_probability: 53 changes
📝 hubspot_owner_id: 9 changes
📝 amount: 5 changes
🔔 Event: creation
Count: 17
🔔 Event: association_change
Count: 119
INFO Looking for metrics: Config 537 (Mobiz - 563), Date 2026-04-16.
📊 Webhook Metrics for Config 537 (Mobiz - 563)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: creation
Count: 19
🔔 Event: property_change
Count: 751
Properties:
📝 email: 26 changes
📝 lastname: 18 changes
📝 phone: 15 changes
📝 hubspot_owner_id: 630 changes
📝 firstname: 21 changes
📝 country: 9 changes
📝 jobtitle: 14 changes
📝 associatedcompanyid: 15 changes
📝 mobilephone: 3 changes
🔔 Event: association_change
Count: 35
📦 Object Type: company
🔔 Event: association_change
Count: 36
🔔 Event: property_change
Count: 24
Properties:
📝 hubspot_owner_id: 6 changes
📝 domain: 5 changes
📝 country: 4 changes
📝 industry: 4 changes
📝 name: 4 changes
📝 phone: 1 changes
🔔 Event: creation
Count: 4
📦 Object Type: deal
🔔 Event: property_change
Count: 1
Properties:
📝 hs_deal_stage_probability: 1 changes
🔔 Event: association_change
Count: 3
🔔 Event: creation
Count: 1
INFO Looking for metrics: Config 428 (Welcome to the Jungle UK - 461), Date 2026-04-16.
📊 Webhook Metrics for Config 428 (Welcome to the Jungle UK - 461)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 107
🔔 Event: property_change
Count: 352
Properties:
📝 firstname: 34 changes
📝 email: 35 changes
📝 lastname: 32 changes
📝 associatedcompanyid: 37 changes
📝 country: 29 changes
📝 jobtitle: 29 changes
📝 phone: 43 changes
📝 hubspot_owner_id: 108 changes
📝 mobilephone: 5 changes
🔔 Event: creation
Count: 32
📦 Object Type: deal
🔔 Event: property_change
Count: 225
Properties:
📝 hs_deal_stage_probability: 53 changes
📝 product: 7 changes
📝 closedate: 20 changes
📝 dealstage: 40 changes
📝 hs_manual_forecast_category: 31 changes
📝 amount: 26 changes
📝 deal_currency_code: 17 changes
📝 dealname: 5 changes
📝 segment: 16 changes
📝 hs_next_step: 6 changes
📝 deal_source: 4 changes
🔔 Event: creation
Count: 14
🔔 Event: association_change
Count: 41
📦 Object Type: company
🔔 Event: property_change
Count: 34
Properties:
📝 hubspot_owner_id: 18 changes
📝 domain: 9 changes
📝 name: 7 changes
🔔 Event: creation
Count: 9
🔔 Event: association_change
Count: 130
INFO Looking for metrics: Config 581 (Penfold - 606), Date 2026-04-16.
📊 Webhook Metrics for Config 581 (Penfold - 606)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 161
🔔 Event: creation
Count: 117
🔔 Event: property_change
Count: 574
Properties:
📝 email: 117 changes
📝 lastname: 69 changes
📝 firstname: 74 changes
📝 associatedcompanyid: 74 changes
📝 hubspot_owner_id: 156 changes
📝 jobtitle: 28 changes
📝 country: 22 changes
📝 phone: 18 changes
📝 mobilephone: 16 changes
📦 Object Type: company
🔔 Event: creation
Count: 21
🔔 Event: property_change
Count: 78
Properties:
📝 domain: 22 changes
📝 name: 23 changes
📝 hubspot_owner_id: 27 changes
📝 phone: 3 changes
📝 industry: 1 changes
📝 country: 2 changes
🔔 Event: association_change
Count: 167
📦 Object Type: deal
🔔 Event: association_change
Count: 18
🔔 Event: creation
Count: 6
🔔 Event: property_change
Count: 49
Properties:
📝 dealstage: 13 changes
📝 hs_deal_stage_probability: 18 changes
📝 hs_manual_forecast_category: 10 changes
📝 hubspot_owner_id: 2 changes
📝 hs_next_step: 1 changes
📝 amount: 2 changes
📝 closedate: 3 changes
INFO Looking for metrics: Config 1015 (Travefy - 1049), Date 2026-04-16.
📊 Webhook Metrics for Config 1015 (Travefy - 1049)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: property_change
Count: 1064
Properties:
📝 firstname: 194 changes
📝 phone: 141 changes
📝 email: 156 changes
📝 lastname: 171 changes
📝 associatedcompanyid: 156 changes
📝 hubspot_owner_id: 228 changes
📝 jobtitle: 11 changes
📝 mobilephone: 5 changes
📝 country: 2 changes
🔔 Event: creation
Count: 154
🔔 Event: association_change
Count: 411
📦 Object Type: deal
🔔 Event: property_change
Count: 1072
Properties:
📝 deal_currency_code: 98 changes
📝 amount: 108 changes
📝 hs_deal_stage_probability: 262 changes
📝 hs_manual_forecast_category: 217 changes
📝 hubspot_owner_id: 107 changes
📝 closedate: 109 changes
📝 dealstage: 163 changes
📝 seats_interest: 2 changes
📝 pipeline: 5 changes
📝 competitor_deal: 1 changes
🔔 Event: creation
Count: 99
🔔 Event: association_change
Count: 297
📦 Object Type: company
🔔 Event: creation
Count: 151
🔔 Event: association_change
Count: 510
🔔 Event: property_change
Count: 263
Properties:
📝 name: 171 changes
📝 hubspot_owner_id: 92 changes
INFO Looking for metrics: Config 413 (VCC - 347), Date 2026-04-16.
DOCKER
Close Tab
-zsh
Close Tab
-zsh
Close Tab
✳ Build full day activity summary from Screenpipe (claude)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
APP (-zsh)
Close Tab
ec2-user@ip-10-30-159-186:~ (nc)
Close Tab
⌥⌘1
ec2-user@ip-10-30-159-186:~...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"Properties:\n 📝\u0000 hubspot_owner_id: 2837 changes\n 📝\u0000 email: 4471 changes\n 📝\u0000 phone: 3661 changes\n 📝\u0000 lastname: 16150 changes\n 📝\u0000 firstname: 22622 changes\n 📝\u0000 associatedcompanyid: 4860 changes\n 📝\u0000 mobilephone: 13 changes\n 📝\u0000 jobtitle: 5 changes\n 📝\u0000 country: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4471\n\n 🔔\u0000 Event: association_change\n Count: 17648\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 7588\n\n 🔔\u0000 Event: property_change\n Count: 82720\n Properties:\n 📝\u0000 dealname: 4204 changes\n 📝\u0000 amount: 22594 changes\n 📝\u0000 hs_deal_stage_probability: 17646 changes\n 📝\u0000 pipeline: 4356 changes\n 📝\u0000 dealstage: 14626 changes\n 📝\u0000 closedate: 11096 changes\n 📝\u0000 hubspot_owner_id: 6094 changes\n 📝\u0000 selected_date: 1969 changes\n 📝\u0000 deal_currency_code: 135 changes\n\n 🔔\u0000 Event: association_change\n Count: 22773\n\n\n INFO Looking for metrics: Config 878 (Dingus and Zazzy - 929), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 878 (Dingus and Zazzy - 929)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 158\n Properties:\n 📝\u0000 amount_in_home_currency: 24 changes\n 📝\u0000 hs_deal_stage_probability: 35 changes\n 📝\u0000 hs_manual_forecast_category: 27 changes\n 📝\u0000 closedate: 23 changes\n 📝\u0000 lost_reason: 9 changes\n 📝\u0000 dealstage: 27 changes\n 📝\u0000 dealname: 9 changes\n 📝\u0000 hubspot_owner_id: 2 changes\n 📝\u0000 source_attribution: 2 changes\n\n 🔔\u0000 Event: creation\n Count: 18\n\n 🔔\u0000 Event: association_change\n Count: 52\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 59\n Properties:\n 📝\u0000 domain: 14 changes\n 📝\u0000 name: 14 changes\n 📝\u0000 hubspot_owner_id: 17 changes\n 📝\u0000 industry: 5 changes\n 📝\u0000 phone: 4 changes\n 📝\u0000 country: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 69\n\n 🔔\u0000 Event: creation\n Count: 14\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 192\n Properties:\n 📝\u0000 phone: 8 changes\n 📝\u0000 lastname: 22 changes\n 📝\u0000 firstname: 22 changes\n 📝\u0000 jobtitle: 20 changes\n 📝\u0000 hubspot_owner_id: 76 changes\n 📝\u0000 email: 17 changes\n 📝\u0000 associatedcompanyid: 16 changes\n 📝\u0000 mobilephone: 11 changes\n\n 🔔\u0000 Event: association_change\n Count: 53\n\n 🔔\u0000 Event: creation\n Count: 20\n\n\n INFO Looking for metrics: Config 671 (CosmosID - 691), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 671 (CosmosID - 691)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 5\n\n 🔔\u0000 Event: property_change\n Count: 15\n Properties:\n 📝\u0000 hubspot_owner_id: 6 changes\n 📝\u0000 name: 5 changes\n 📝\u0000 domain: 3 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 44\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 78\n Properties:\n 📝\u0000 hs_deal_stage_probability: 14 changes\n 📝\u0000 createdate: 10 changes\n 📝\u0000 hs_manual_forecast_category: 13 changes\n 📝\u0000 deal_currency_code: 4 changes\n 📝\u0000 amount: 20 changes\n 📝\u0000 closedate: 8 changes\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 dealtype: 1 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 30\n\n 🔔\u0000 Event: creation\n Count: 10\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 13\n\n 🔔\u0000 Event: property_change\n Count: 145\n Properties:\n 📝\u0000 jobtitle: 77 changes\n 📝\u0000 hubspot_owner_id: 12 changes\n 📝\u0000 firstname: 9 changes\n 📝\u0000 phone: 5 changes\n 📝\u0000 lastname: 8 changes\n 📝\u0000 email: 13 changes\n 📝\u0000 associatedcompanyid: 12 changes\n 📝\u0000 country: 9 changes\n\n 🔔\u0000 Event: association_change\n Count: 34\n\n\n INFO Looking for metrics: Config 652 (Abode - 673), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 652 (Abode - 673)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 1\n Properties:\n 📝\u0000 country: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 10\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 6\n\n 🔔\u0000 Event: property_change\n Count: 40\n Properties:\n 📝\u0000 phone: 3 changes\n 📝\u0000 hubspot_owner_id: 8 changes\n 📝\u0000 email: 6 changes\n 📝\u0000 associatedcompanyid: 4 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 lastname: 5 changes\n 📝\u0000 jobtitle: 4 changes\n 📝\u0000 firstname: 5 changes\n 📝\u0000 mobilephone: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 9\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 1\n\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: property_change\n Count: 13\n Properties:\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 hs_deal_stage_probability: 5 changes\n 📝\u0000 dealname: 1 changes\n 📝\u0000 closedate: 1 changes\n 📝\u0000 hs_manual_forecast_category: 1 changes\n 📝\u0000 amount: 1 changes\n\n\n INFO Looking for metrics: Config 1049 (Classavo - 851), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1049 (Classavo - 851)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 2\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 1\n\n 🔔\u0000 Event: property_change\n Count: 3\n Properties:\n 📝\u0000 firstname: 1 changes\n 📝\u0000 lastname: 1 changes\n 📝\u0000 jobtitle: 1 changes\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: property_change\n Count: 14\n Properties:\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 hs_deal_stage_probability: 5 changes\n 📝\u0000 closedate: 3 changes\n 📝\u0000 deal_currency_code: 1 changes\n 📝\u0000 amount: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 1\n\n\n INFO Looking for metrics: Config 290 (D1 Training - 308), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 290 (D1 Training - 308)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 5\n Properties:\n 📝\u0000 name: 4 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 1\n\n 🔔\u0000 Event: association_change\n Count: 6\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 40\n\n 🔔\u0000 Event: creation\n Count: 35\n\n 🔔\u0000 Event: property_change\n Count: 405\n Properties:\n 📝\u0000 hs_deal_stage_probability: 149 changes\n 📝\u0000 dealtype: 34 changes\n 📝\u0000 dealstage: 114 changes\n 📝\u0000 hubspot_owner_id: 5 changes\n 📝\u0000 closedate: 97 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 pipeline: 2 changes\n 📝\u0000 hs_manual_forecast_category: 1 changes\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 50\n\n 🔔\u0000 Event: property_change\n Count: 314\n Properties:\n 📝\u0000 lastname: 53 changes\n 📝\u0000 email: 46 changes\n 📝\u0000 firstname: 58 changes\n 📝\u0000 mobilephone: 26 changes\n 📝\u0000 phone: 70 changes\n 📝\u0000 hubspot_owner_id: 59 changes\n 📝\u0000 associatedcompanyid: 2 changes\n\n 🔔\u0000 Event: association_change\n Count: 42\n\n\n INFO Looking for metrics: Config 1019 (SimpleConsign - 1088), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1019 (SimpleConsign - 1088)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 692\n\n 🔔\u0000 Event: property_change\n Count: 2659\n Properties:\n 📝\u0000 email: 334 changes\n 📝\u0000 hubspot_owner_id: 980 changes\n 📝\u0000 associatedcompanyid: 327 changes\n 📝\u0000 lastname: 332 changes\n 📝\u0000 phone: 20 changes\n 📝\u0000 firstname: 333 changes\n 📝\u0000 jobtitle: 321 changes\n 📝\u0000 mobilephone: 7 changes\n 📝\u0000 country: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 335\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 698\n\n 🔔\u0000 Event: property_change\n Count: 637\n Properties:\n 📝\u0000 domain: 310 changes\n 📝\u0000 name: 312 changes\n 📝\u0000 phone: 5 changes\n 📝\u0000 hubspot_owner_id: 4 changes\n 📝\u0000 country: 3 changes\n 📝\u0000 industry: 3 changes\n\n 🔔\u0000 Event: creation\n Count: 312\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 46\n\n 🔔\u0000 Event: creation\n Count: 9\n\n\n INFO Looking for metrics: Config 311 (Lemon.io - 329), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 311 (Lemon.io - 329)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 128\n Properties:\n 📝\u0000 domain: 26 changes\n 📝\u0000 name: 24 changes\n 📝\u0000 industry: 18 changes\n 📝\u0000 country: 17 changes\n 📝\u0000 hubspot_owner_id: 32 changes\n 📝\u0000 phone: 11 changes\n\n 🔔\u0000 Event: creation\n Count: 26\n\n 🔔\u0000 Event: association_change\n Count: 120\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 16\n\n 🔔\u0000 Event: property_change\n Count: 385\n Properties:\n 📝\u0000 closedate: 21 changes\n 📝\u0000 dealstage: 49 changes\n 📝\u0000 days_to_close: 19 changes\n 📝\u0000 hs_deal_stage_probability: 58 changes\n 📝\u0000 hs_manual_forecast_category: 34 changes\n 📝\u0000 hs_next_step: 14 changes\n 📝\u0000 level_of_involvement: 15 changes\n 📝\u0000 hubspot_owner_id: 20 changes\n 📝\u0000 freelancers_under_review: 31 changes\n 📝\u0000 pipeline: 8 changes\n 📝\u0000 dealname: 30 changes\n 📝\u0000 deal_currency_code: 8 changes\n 📝\u0000 amount: 13 changes\n 📝\u0000 hs_priority: 8 changes\n 📝\u0000 budget_limitations: 6 changes\n 📝\u0000 need: 7 changes\n 📝\u0000 positive_outcome: 6 changes\n 📝\u0000 competition: 6 changes\n 📝\u0000 negative_consequences: 6 changes\n 📝\u0000 selection_process: 8 changes\n 📝\u0000 project_team_structure: 7 changes\n 📝\u0000 champion: 6 changes\n 📝\u0000 red_flags: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 63\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 336\n Properties:\n 📝\u0000 hubspot_owner_id: 74 changes\n 📝\u0000 email: 56 changes\n 📝\u0000 firstname: 52 changes\n 📝\u0000 lastname: 48 changes\n 📝\u0000 country: 21 changes\n 📝\u0000 jobtitle: 26 changes\n 📝\u0000 associatedcompanyid: 39 changes\n 📝\u0000 phone: 15 changes\n 📝\u0000 mobilephone: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 103\n\n 🔔\u0000 Event: creation\n Count: 54\n\n\n INFO Looking for metrics: Config 802 (Street Group - 853), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 802 (Street Group - 853)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 252\n\n 🔔\u0000 Event: association_change\n Count: 558\n\n 🔔\u0000 Event: property_change\n Count: 1830\n Properties:\n 📝\u0000 phone: 170 changes\n 📝\u0000 lastname: 306 changes\n 📝\u0000 firstname: 318 changes\n 📝\u0000 email: 297 changes\n 📝\u0000 country: 287 changes\n 📝\u0000 jobtitle: 208 changes\n 📝\u0000 associatedcompanyid: 180 changes\n 📝\u0000 hubspot_owner_id: 52 changes\n 📝\u0000 mobilephone: 12 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 617\n\n 🔔\u0000 Event: creation\n Count: 31\n\n 🔔\u0000 Event: property_change\n Count: 94\n Properties:\n 📝\u0000 industry: 6 changes\n 📝\u0000 country: 10 changes\n 📝\u0000 phone: 10 changes\n 📝\u0000 name: 22 changes\n 📝\u0000 domain: 35 changes\n 📝\u0000 hubspot_owner_id: 11 changes\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 121\n\n 🔔\u0000 Event: property_change\n Count: 338\n Properties:\n 📝\u0000 hs_deal_stage_probability: 78 changes\n 📝\u0000 deal_currency_code: 22 changes\n 📝\u0000 amount: 36 changes\n 📝\u0000 closedate: 42 changes\n 📝\u0000 dealstage: 91 changes\n 📝\u0000 dealtype: 2 changes\n 📝\u0000 hs_manual_forecast_category: 28 changes\n 📝\u0000 dealname: 8 changes\n 📝\u0000 hs_forecast_probability: 16 changes\n 📝\u0000 invoice_start_date: 8 changes\n 📝\u0000 hubspot_owner_id: 7 changes\n\n 🔔\u0000 Event: creation\n Count: 23\n\n\n INFO Looking for metrics: Config 1053 (Sensi.AI - 1117), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1053 (Sensi.AI - 1117)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 156\n Properties:\n 📝\u0000 domain: 32 changes\n 📝\u0000 phone: 23 changes\n 📝\u0000 industry: 27 changes\n 📝\u0000 country: 29 changes\n 📝\u0000 name: 39 changes\n 📝\u0000 hubspot_owner_id: 6 changes\n\n 🔔\u0000 Event: creation\n Count: 39\n\n 🔔\u0000 Event: association_change\n Count: 1499\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 491\n Properties:\n 📝\u0000 hs_deal_stage_probability: 96 changes\n 📝\u0000 hs_next_step: 46 changes\n 📝\u0000 next_steps_jiminny: 38 changes\n 📝\u0000 hubspot_owner_id: 17 changes\n 📝\u0000 monthly_billable_hours: 10 changes\n 📝\u0000 closedate: 18 changes\n 📝\u0000 amount: 119 changes\n 📝\u0000 deal_currency_code: 115 changes\n 📝\u0000 dealstage: 23 changes\n 📝\u0000 dealname: 4 changes\n 📝\u0000 client_segmentation_jiminny: 1 changes\n 📝\u0000 pain_points__main_objections: 1 changes\n 📝\u0000 product_feedback: 1 changes\n 📝\u0000 red_flags: 1 changes\n 📝\u0000 competitors: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 98\n\n 🔔\u0000 Event: creation\n Count: 74\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 1577\n\n 🔔\u0000 Event: property_change\n Count: 3768\n Properties:\n 📝\u0000 email: 857 changes\n 📝\u0000 mobilephone: 104 changes\n 📝\u0000 lastname: 854 changes\n 📝\u0000 jobtitle: 98 changes\n 📝\u0000 phone: 130 changes\n 📝\u0000 firstname: 870 changes\n 📝\u0000 hubspot_owner_id: 92 changes\n 📝\u0000 associatedcompanyid: 737 changes\n 📝\u0000 country: 26 changes\n\n 🔔\u0000 Event: creation\n Count: 872\n\n\n INFO Looking for metrics: Config 87 (Repsly - 93), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 87 (Repsly - 93)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 295\n\n 🔔\u0000 Event: property_change\n Count: 1285\n Properties:\n 📝\u0000 lastname: 150 changes\n 📝\u0000 email: 128 changes\n 📝\u0000 firstname: 162 changes\n 📝\u0000 associatedcompanyid: 145 changes\n 📝\u0000 country: 130 changes\n 📝\u0000 hubspot_owner_id: 175 changes\n 📝\u0000 mobilephone: 203 changes\n 📝\u0000 phone: 60 changes\n 📝\u0000 jobtitle: 132 changes\n\n 🔔\u0000 Event: creation\n Count: 159\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 3\n\n 🔔\u0000 Event: association_change\n Count: 13\n\n 🔔\u0000 Event: property_change\n Count: 23\n Properties:\n 📝\u0000 hs_deal_stage_probability: 4 changes\n 📝\u0000 amount: 14 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n 📝\u0000 closedate: 2 changes\n 📝\u0000 hs_next_step: 1 changes\n 📝\u0000 dealstage: 1 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 298\n\n 🔔\u0000 Event: property_change\n Count: 23\n Properties:\n 📝\u0000 domain: 9 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 name: 6 changes\n 📝\u0000 hubspot_owner_id: 3 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 9\n\n\n INFO Looking for metrics: Config 518 (Prolific - 544), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 518 (Prolific - 544)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 14\n Properties:\n 📝\u0000 amount: 3 changes\n 📝\u0000 hs_deal_stage_probability: 4 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 closedate: 3 changes\n 📝\u0000 dealstage: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 3\n\n 🔔\u0000 Event: association_change\n Count: 9\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 534\n\n 🔔\u0000 Event: property_change\n Count: 7533\n Properties:\n 📝\u0000 jobtitle: 73 changes\n 📝\u0000 phone: 2 changes\n 📝\u0000 email: 561 changes\n 📝\u0000 lastname: 81 changes\n 📝\u0000 firstname: 90 changes\n 📝\u0000 associatedcompanyid: 166 changes\n 📝\u0000 hubspot_owner_id: 6151 changes\n 📝\u0000 country: 409 changes\n\n 🔔\u0000 Event: association_change\n Count: 349\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 30\n\n 🔔\u0000 Event: association_change\n Count: 352\n\n 🔔\u0000 Event: property_change\n Count: 575\n Properties:\n 📝\u0000 domain: 30 changes\n 📝\u0000 country: 421 changes\n 📝\u0000 phone: 12 changes\n 📝\u0000 industry: 18 changes\n 📝\u0000 name: 23 changes\n 📝\u0000 hubspot_owner_id: 71 changes\n\n\n INFO Looking for metrics: Config 761 (Ressio Software - 770), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 761 (Ressio Software - 770)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 1370\n Properties:\n 📝\u0000 hubspot_owner_id: 471 changes\n 📝\u0000 domain: 199 changes\n 📝\u0000 name: 202 changes\n 📝\u0000 country: 163 changes\n 📝\u0000 industry: 170 changes\n 📝\u0000 phone: 165 changes\n\n 🔔\u0000 Event: creation\n Count: 207\n\n 🔔\u0000 Event: association_change\n Count: 435\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 1582\n Properties:\n 📝\u0000 hubspot_owner_id: 476 changes\n 📝\u0000 phone: 155 changes\n 📝\u0000 mobilephone: 97 changes\n 📝\u0000 lastname: 153 changes\n 📝\u0000 email: 109 changes\n 📝\u0000 associatedcompanyid: 160 changes\n 📝\u0000 firstname: 163 changes\n 📝\u0000 jobtitle: 139 changes\n 📝\u0000 country: 130 changes\n\n 🔔\u0000 Event: creation\n Count: 152\n\n 🔔\u0000 Event: association_change\n Count: 386\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 119\n Properties:\n 📝\u0000 closedate: 16 changes\n 📝\u0000 dealstage: 36 changes\n 📝\u0000 hs_deal_stage_probability: 53 changes\n 📝\u0000 hubspot_owner_id: 9 changes\n 📝\u0000 amount: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 17\n\n 🔔\u0000 Event: association_change\n Count: 119\n\n\n INFO Looking for metrics: Config 537 (Mobiz - 563), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 537 (Mobiz - 563)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 19\n\n 🔔\u0000 Event: property_change\n Count: 751\n Properties:\n 📝\u0000 email: 26 changes\n 📝\u0000 lastname: 18 changes\n 📝\u0000 phone: 15 changes\n 📝\u0000 hubspot_owner_id: 630 changes\n 📝\u0000 firstname: 21 changes\n 📝\u0000 country: 9 changes\n 📝\u0000 jobtitle: 14 changes\n 📝\u0000 associatedcompanyid: 15 changes\n 📝\u0000 mobilephone: 3 changes\n\n 🔔\u0000 Event: association_change\n Count: 35\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 36\n\n 🔔\u0000 Event: property_change\n Count: 24\n Properties:\n 📝\u0000 hubspot_owner_id: 6 changes\n 📝\u0000 domain: 5 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 industry: 4 changes\n 📝\u0000 name: 4 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 1\n Properties:\n 📝\u0000 hs_deal_stage_probability: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: creation\n Count: 1\n\n\n INFO Looking for metrics: Config 428 (Welcome to the Jungle UK - 461), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 428 (Welcome to the Jungle UK - 461)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 107\n\n 🔔\u0000 Event: property_change\n Count: 352\n Properties:\n 📝\u0000 firstname: 34 changes\n 📝\u0000 email: 35 changes\n 📝\u0000 lastname: 32 changes\n 📝\u0000 associatedcompanyid: 37 changes\n 📝\u0000 country: 29 changes\n 📝\u0000 jobtitle: 29 changes\n 📝\u0000 phone: 43 changes\n 📝\u0000 hubspot_owner_id: 108 changes\n 📝\u0000 mobilephone: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 32\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 225\n Properties:\n 📝\u0000 hs_deal_stage_probability: 53 changes\n 📝\u0000 product: 7 changes\n 📝\u0000 closedate: 20 changes\n 📝\u0000 dealstage: 40 changes\n 📝\u0000 hs_manual_forecast_category: 31 changes\n 📝\u0000 amount: 26 changes\n 📝\u0000 deal_currency_code: 17 changes\n 📝\u0000 dealname: 5 changes\n 📝\u0000 segment: 16 changes\n 📝\u0000 hs_next_step: 6 changes\n 📝\u0000 deal_source: 4 changes\n\n 🔔\u0000 Event: creation\n Count: 14\n\n 🔔\u0000 Event: association_change\n Count: 41\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 34\n Properties:\n 📝\u0000 hubspot_owner_id: 18 changes\n 📝\u0000 domain: 9 changes\n 📝\u0000 name: 7 changes\n\n 🔔\u0000 Event: creation\n Count: 9\n\n 🔔\u0000 Event: association_change\n Count: 130\n\n\n INFO Looking for metrics: Config 581 (Penfold - 606), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 581 (Penfold - 606)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 161\n\n 🔔\u0000 Event: creation\n Count: 117\n\n 🔔\u0000 Event: property_change\n Count: 574\n Properties:\n 📝\u0000 email: 117 changes\n 📝\u0000 lastname: 69 changes\n 📝\u0000 firstname: 74 changes\n 📝\u0000 associatedcompanyid: 74 changes\n 📝\u0000 hubspot_owner_id: 156 changes\n 📝\u0000 jobtitle: 28 changes\n 📝\u0000 country: 22 changes\n 📝\u0000 phone: 18 changes\n 📝\u0000 mobilephone: 16 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 21\n\n 🔔\u0000 Event: property_change\n Count: 78\n Properties:\n 📝\u0000 domain: 22 changes\n 📝\u0000 name: 23 changes\n 📝\u0000 hubspot_owner_id: 27 changes\n 📝\u0000 phone: 3 changes\n 📝\u0000 industry: 1 changes\n 📝\u0000 country: 2 changes\n\n 🔔\u0000 Event: association_change\n Count: 167\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 18\n\n 🔔\u0000 Event: creation\n Count: 6\n\n 🔔\u0000 Event: property_change\n Count: 49\n Properties:\n 📝\u0000 dealstage: 13 changes\n 📝\u0000 hs_deal_stage_probability: 18 changes\n 📝\u0000 hs_manual_forecast_category: 10 changes\n 📝\u0000 hubspot_owner_id: 2 changes\n 📝\u0000 hs_next_step: 1 changes\n 📝\u0000 amount: 2 changes\n 📝\u0000 closedate: 3 changes\n\n\n INFO Looking for metrics: Config 1015 (Travefy - 1049), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1015 (Travefy - 1049)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 1064\n Properties:\n 📝\u0000 firstname: 194 changes\n 📝\u0000 phone: 141 changes\n 📝\u0000 email: 156 changes\n 📝\u0000 lastname: 171 changes\n 📝\u0000 associatedcompanyid: 156 changes\n 📝\u0000 hubspot_owner_id: 228 changes\n 📝\u0000 jobtitle: 11 changes\n 📝\u0000 mobilephone: 5 changes\n 📝\u0000 country: 2 changes\n\n 🔔\u0000 Event: creation\n Count: 154\n\n 🔔\u0000 Event: association_change\n Count: 411\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 1072\n Properties:\n 📝\u0000 deal_currency_code: 98 changes\n 📝\u0000 amount: 108 changes\n 📝\u0000 hs_deal_stage_probability: 262 changes\n 📝\u0000 hs_manual_forecast_category: 217 changes\n 📝\u0000 hubspot_owner_id: 107 changes\n 📝\u0000 closedate: 109 changes\n 📝\u0000 dealstage: 163 changes\n 📝\u0000 seats_interest: 2 changes\n 📝\u0000 pipeline: 5 changes\n 📝\u0000 competitor_deal: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 99\n\n 🔔\u0000 Event: association_change\n Count: 297\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 151\n\n 🔔\u0000 Event: association_change\n Count: 510\n\n 🔔\u0000 Event: property_change\n Count: 263\n Properties:\n 📝\u0000 name: 171 changes\n 📝\u0000 hubspot_owner_id: 92 changes\n\n\n INFO Looking for metrics: Config 413 (VCC - 347), Date 2026-04-16.","depth":4,"value":"Properties:\n 📝\u0000 hubspot_owner_id: 2837 changes\n 📝\u0000 email: 4471 changes\n 📝\u0000 phone: 3661 changes\n 📝\u0000 lastname: 16150 changes\n 📝\u0000 firstname: 22622 changes\n 📝\u0000 associatedcompanyid: 4860 changes\n 📝\u0000 mobilephone: 13 changes\n 📝\u0000 jobtitle: 5 changes\n 📝\u0000 country: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4471\n\n 🔔\u0000 Event: association_change\n Count: 17648\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 7588\n\n 🔔\u0000 Event: property_change\n Count: 82720\n Properties:\n 📝\u0000 dealname: 4204 changes\n 📝\u0000 amount: 22594 changes\n 📝\u0000 hs_deal_stage_probability: 17646 changes\n 📝\u0000 pipeline: 4356 changes\n 📝\u0000 dealstage: 14626 changes\n 📝\u0000 closedate: 11096 changes\n 📝\u0000 hubspot_owner_id: 6094 changes\n 📝\u0000 selected_date: 1969 changes\n 📝\u0000 deal_currency_code: 135 changes\n\n 🔔\u0000 Event: association_change\n Count: 22773\n\n\n INFO Looking for metrics: Config 878 (Dingus and Zazzy - 929), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 878 (Dingus and Zazzy - 929)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 158\n Properties:\n 📝\u0000 amount_in_home_currency: 24 changes\n 📝\u0000 hs_deal_stage_probability: 35 changes\n 📝\u0000 hs_manual_forecast_category: 27 changes\n 📝\u0000 closedate: 23 changes\n 📝\u0000 lost_reason: 9 changes\n 📝\u0000 dealstage: 27 changes\n 📝\u0000 dealname: 9 changes\n 📝\u0000 hubspot_owner_id: 2 changes\n 📝\u0000 source_attribution: 2 changes\n\n 🔔\u0000 Event: creation\n Count: 18\n\n 🔔\u0000 Event: association_change\n Count: 52\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 59\n Properties:\n 📝\u0000 domain: 14 changes\n 📝\u0000 name: 14 changes\n 📝\u0000 hubspot_owner_id: 17 changes\n 📝\u0000 industry: 5 changes\n 📝\u0000 phone: 4 changes\n 📝\u0000 country: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 69\n\n 🔔\u0000 Event: creation\n Count: 14\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 192\n Properties:\n 📝\u0000 phone: 8 changes\n 📝\u0000 lastname: 22 changes\n 📝\u0000 firstname: 22 changes\n 📝\u0000 jobtitle: 20 changes\n 📝\u0000 hubspot_owner_id: 76 changes\n 📝\u0000 email: 17 changes\n 📝\u0000 associatedcompanyid: 16 changes\n 📝\u0000 mobilephone: 11 changes\n\n 🔔\u0000 Event: association_change\n Count: 53\n\n 🔔\u0000 Event: creation\n Count: 20\n\n\n INFO Looking for metrics: Config 671 (CosmosID - 691), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 671 (CosmosID - 691)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 5\n\n 🔔\u0000 Event: property_change\n Count: 15\n Properties:\n 📝\u0000 hubspot_owner_id: 6 changes\n 📝\u0000 name: 5 changes\n 📝\u0000 domain: 3 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 44\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 78\n Properties:\n 📝\u0000 hs_deal_stage_probability: 14 changes\n 📝\u0000 createdate: 10 changes\n 📝\u0000 hs_manual_forecast_category: 13 changes\n 📝\u0000 deal_currency_code: 4 changes\n 📝\u0000 amount: 20 changes\n 📝\u0000 closedate: 8 changes\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 dealtype: 1 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 30\n\n 🔔\u0000 Event: creation\n Count: 10\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 13\n\n 🔔\u0000 Event: property_change\n Count: 145\n Properties:\n 📝\u0000 jobtitle: 77 changes\n 📝\u0000 hubspot_owner_id: 12 changes\n 📝\u0000 firstname: 9 changes\n 📝\u0000 phone: 5 changes\n 📝\u0000 lastname: 8 changes\n 📝\u0000 email: 13 changes\n 📝\u0000 associatedcompanyid: 12 changes\n 📝\u0000 country: 9 changes\n\n 🔔\u0000 Event: association_change\n Count: 34\n\n\n INFO Looking for metrics: Config 652 (Abode - 673), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 652 (Abode - 673)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 1\n Properties:\n 📝\u0000 country: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 10\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 6\n\n 🔔\u0000 Event: property_change\n Count: 40\n Properties:\n 📝\u0000 phone: 3 changes\n 📝\u0000 hubspot_owner_id: 8 changes\n 📝\u0000 email: 6 changes\n 📝\u0000 associatedcompanyid: 4 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 lastname: 5 changes\n 📝\u0000 jobtitle: 4 changes\n 📝\u0000 firstname: 5 changes\n 📝\u0000 mobilephone: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 9\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 1\n\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: property_change\n Count: 13\n Properties:\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 hs_deal_stage_probability: 5 changes\n 📝\u0000 dealname: 1 changes\n 📝\u0000 closedate: 1 changes\n 📝\u0000 hs_manual_forecast_category: 1 changes\n 📝\u0000 amount: 1 changes\n\n\n INFO Looking for metrics: Config 1049 (Classavo - 851), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1049 (Classavo - 851)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 2\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 1\n\n 🔔\u0000 Event: property_change\n Count: 3\n Properties:\n 📝\u0000 firstname: 1 changes\n 📝\u0000 lastname: 1 changes\n 📝\u0000 jobtitle: 1 changes\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: property_change\n Count: 14\n Properties:\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 hs_deal_stage_probability: 5 changes\n 📝\u0000 closedate: 3 changes\n 📝\u0000 deal_currency_code: 1 changes\n 📝\u0000 amount: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 1\n\n\n INFO Looking for metrics: Config 290 (D1 Training - 308), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 290 (D1 Training - 308)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 5\n Properties:\n 📝\u0000 name: 4 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 1\n\n 🔔\u0000 Event: association_change\n Count: 6\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 40\n\n 🔔\u0000 Event: creation\n Count: 35\n\n 🔔\u0000 Event: property_change\n Count: 405\n Properties:\n 📝\u0000 hs_deal_stage_probability: 149 changes\n 📝\u0000 dealtype: 34 changes\n 📝\u0000 dealstage: 114 changes\n 📝\u0000 hubspot_owner_id: 5 changes\n 📝\u0000 closedate: 97 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 pipeline: 2 changes\n 📝\u0000 hs_manual_forecast_category: 1 changes\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 50\n\n 🔔\u0000 Event: property_change\n Count: 314\n Properties:\n 📝\u0000 lastname: 53 changes\n 📝\u0000 email: 46 changes\n 📝\u0000 firstname: 58 changes\n 📝\u0000 mobilephone: 26 changes\n 📝\u0000 phone: 70 changes\n 📝\u0000 hubspot_owner_id: 59 changes\n 📝\u0000 associatedcompanyid: 2 changes\n\n 🔔\u0000 Event: association_change\n Count: 42\n\n\n INFO Looking for metrics: Config 1019 (SimpleConsign - 1088), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1019 (SimpleConsign - 1088)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 692\n\n 🔔\u0000 Event: property_change\n Count: 2659\n Properties:\n 📝\u0000 email: 334 changes\n 📝\u0000 hubspot_owner_id: 980 changes\n 📝\u0000 associatedcompanyid: 327 changes\n 📝\u0000 lastname: 332 changes\n 📝\u0000 phone: 20 changes\n 📝\u0000 firstname: 333 changes\n 📝\u0000 jobtitle: 321 changes\n 📝\u0000 mobilephone: 7 changes\n 📝\u0000 country: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 335\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 698\n\n 🔔\u0000 Event: property_change\n Count: 637\n Properties:\n 📝\u0000 domain: 310 changes\n 📝\u0000 name: 312 changes\n 📝\u0000 phone: 5 changes\n 📝\u0000 hubspot_owner_id: 4 changes\n 📝\u0000 country: 3 changes\n 📝\u0000 industry: 3 changes\n\n 🔔\u0000 Event: creation\n Count: 312\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 46\n\n 🔔\u0000 Event: creation\n Count: 9\n\n\n INFO Looking for metrics: Config 311 (Lemon.io - 329), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 311 (Lemon.io - 329)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 128\n Properties:\n 📝\u0000 domain: 26 changes\n 📝\u0000 name: 24 changes\n 📝\u0000 industry: 18 changes\n 📝\u0000 country: 17 changes\n 📝\u0000 hubspot_owner_id: 32 changes\n 📝\u0000 phone: 11 changes\n\n 🔔\u0000 Event: creation\n Count: 26\n\n 🔔\u0000 Event: association_change\n Count: 120\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 16\n\n 🔔\u0000 Event: property_change\n Count: 385\n Properties:\n 📝\u0000 closedate: 21 changes\n 📝\u0000 dealstage: 49 changes\n 📝\u0000 days_to_close: 19 changes\n 📝\u0000 hs_deal_stage_probability: 58 changes\n 📝\u0000 hs_manual_forecast_category: 34 changes\n 📝\u0000 hs_next_step: 14 changes\n 📝\u0000 level_of_involvement: 15 changes\n 📝\u0000 hubspot_owner_id: 20 changes\n 📝\u0000 freelancers_under_review: 31 changes\n 📝\u0000 pipeline: 8 changes\n 📝\u0000 dealname: 30 changes\n 📝\u0000 deal_currency_code: 8 changes\n 📝\u0000 amount: 13 changes\n 📝\u0000 hs_priority: 8 changes\n 📝\u0000 budget_limitations: 6 changes\n 📝\u0000 need: 7 changes\n 📝\u0000 positive_outcome: 6 changes\n 📝\u0000 competition: 6 changes\n 📝\u0000 negative_consequences: 6 changes\n 📝\u0000 selection_process: 8 changes\n 📝\u0000 project_team_structure: 7 changes\n 📝\u0000 champion: 6 changes\n 📝\u0000 red_flags: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 63\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 336\n Properties:\n 📝\u0000 hubspot_owner_id: 74 changes\n 📝\u0000 email: 56 changes\n 📝\u0000 firstname: 52 changes\n 📝\u0000 lastname: 48 changes\n 📝\u0000 country: 21 changes\n 📝\u0000 jobtitle: 26 changes\n 📝\u0000 associatedcompanyid: 39 changes\n 📝\u0000 phone: 15 changes\n 📝\u0000 mobilephone: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 103\n\n 🔔\u0000 Event: creation\n Count: 54\n\n\n INFO Looking for metrics: Config 802 (Street Group - 853), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 802 (Street Group - 853)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 252\n\n 🔔\u0000 Event: association_change\n Count: 558\n\n 🔔\u0000 Event: property_change\n Count: 1830\n Properties:\n 📝\u0000 phone: 170 changes\n 📝\u0000 lastname: 306 changes\n 📝\u0000 firstname: 318 changes\n 📝\u0000 email: 297 changes\n 📝\u0000 country: 287 changes\n 📝\u0000 jobtitle: 208 changes\n 📝\u0000 associatedcompanyid: 180 changes\n 📝\u0000 hubspot_owner_id: 52 changes\n 📝\u0000 mobilephone: 12 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 617\n\n 🔔\u0000 Event: creation\n Count: 31\n\n 🔔\u0000 Event: property_change\n Count: 94\n Properties:\n 📝\u0000 industry: 6 changes\n 📝\u0000 country: 10 changes\n 📝\u0000 phone: 10 changes\n 📝\u0000 name: 22 changes\n 📝\u0000 domain: 35 changes\n 📝\u0000 hubspot_owner_id: 11 changes\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 121\n\n 🔔\u0000 Event: property_change\n Count: 338\n Properties:\n 📝\u0000 hs_deal_stage_probability: 78 changes\n 📝\u0000 deal_currency_code: 22 changes\n 📝\u0000 amount: 36 changes\n 📝\u0000 closedate: 42 changes\n 📝\u0000 dealstage: 91 changes\n 📝\u0000 dealtype: 2 changes\n 📝\u0000 hs_manual_forecast_category: 28 changes\n 📝\u0000 dealname: 8 changes\n 📝\u0000 hs_forecast_probability: 16 changes\n 📝\u0000 invoice_start_date: 8 changes\n 📝\u0000 hubspot_owner_id: 7 changes\n\n 🔔\u0000 Event: creation\n Count: 23\n\n\n INFO Looking for metrics: Config 1053 (Sensi.AI - 1117), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1053 (Sensi.AI - 1117)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 156\n Properties:\n 📝\u0000 domain: 32 changes\n 📝\u0000 phone: 23 changes\n 📝\u0000 industry: 27 changes\n 📝\u0000 country: 29 changes\n 📝\u0000 name: 39 changes\n 📝\u0000 hubspot_owner_id: 6 changes\n\n 🔔\u0000 Event: creation\n Count: 39\n\n 🔔\u0000 Event: association_change\n Count: 1499\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 491\n Properties:\n 📝\u0000 hs_deal_stage_probability: 96 changes\n 📝\u0000 hs_next_step: 46 changes\n 📝\u0000 next_steps_jiminny: 38 changes\n 📝\u0000 hubspot_owner_id: 17 changes\n 📝\u0000 monthly_billable_hours: 10 changes\n 📝\u0000 closedate: 18 changes\n 📝\u0000 amount: 119 changes\n 📝\u0000 deal_currency_code: 115 changes\n 📝\u0000 dealstage: 23 changes\n 📝\u0000 dealname: 4 changes\n 📝\u0000 client_segmentation_jiminny: 1 changes\n 📝\u0000 pain_points__main_objections: 1 changes\n 📝\u0000 product_feedback: 1 changes\n 📝\u0000 red_flags: 1 changes\n 📝\u0000 competitors: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 98\n\n 🔔\u0000 Event: creation\n Count: 74\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 1577\n\n 🔔\u0000 Event: property_change\n Count: 3768\n Properties:\n 📝\u0000 email: 857 changes\n 📝\u0000 mobilephone: 104 changes\n 📝\u0000 lastname: 854 changes\n 📝\u0000 jobtitle: 98 changes\n 📝\u0000 phone: 130 changes\n 📝\u0000 firstname: 870 changes\n 📝\u0000 hubspot_owner_id: 92 changes\n 📝\u0000 associatedcompanyid: 737 changes\n 📝\u0000 country: 26 changes\n\n 🔔\u0000 Event: creation\n Count: 872\n\n\n INFO Looking for metrics: Config 87 (Repsly - 93), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 87 (Repsly - 93)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 295\n\n 🔔\u0000 Event: property_change\n Count: 1285\n Properties:\n 📝\u0000 lastname: 150 changes\n 📝\u0000 email: 128 changes\n 📝\u0000 firstname: 162 changes\n 📝\u0000 associatedcompanyid: 145 changes\n 📝\u0000 country: 130 changes\n 📝\u0000 hubspot_owner_id: 175 changes\n 📝\u0000 mobilephone: 203 changes\n 📝\u0000 phone: 60 changes\n 📝\u0000 jobtitle: 132 changes\n\n 🔔\u0000 Event: creation\n Count: 159\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 3\n\n 🔔\u0000 Event: association_change\n Count: 13\n\n 🔔\u0000 Event: property_change\n Count: 23\n Properties:\n 📝\u0000 hs_deal_stage_probability: 4 changes\n 📝\u0000 amount: 14 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n 📝\u0000 closedate: 2 changes\n 📝\u0000 hs_next_step: 1 changes\n 📝\u0000 dealstage: 1 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 298\n\n 🔔\u0000 Event: property_change\n Count: 23\n Properties:\n 📝\u0000 domain: 9 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 name: 6 changes\n 📝\u0000 hubspot_owner_id: 3 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 9\n\n\n INFO Looking for metrics: Config 518 (Prolific - 544), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 518 (Prolific - 544)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 14\n Properties:\n 📝\u0000 amount: 3 changes\n 📝\u0000 hs_deal_stage_probability: 4 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 closedate: 3 changes\n 📝\u0000 dealstage: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 3\n\n 🔔\u0000 Event: association_change\n Count: 9\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 534\n\n 🔔\u0000 Event: property_change\n Count: 7533\n Properties:\n 📝\u0000 jobtitle: 73 changes\n 📝\u0000 phone: 2 changes\n 📝\u0000 email: 561 changes\n 📝\u0000 lastname: 81 changes\n 📝\u0000 firstname: 90 changes\n 📝\u0000 associatedcompanyid: 166 changes\n 📝\u0000 hubspot_owner_id: 6151 changes\n 📝\u0000 country: 409 changes\n\n 🔔\u0000 Event: association_change\n Count: 349\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 30\n\n 🔔\u0000 Event: association_change\n Count: 352\n\n 🔔\u0000 Event: property_change\n Count: 575\n Properties:\n 📝\u0000 domain: 30 changes\n 📝\u0000 country: 421 changes\n 📝\u0000 phone: 12 changes\n 📝\u0000 industry: 18 changes\n 📝\u0000 name: 23 changes\n 📝\u0000 hubspot_owner_id: 71 changes\n\n\n INFO Looking for metrics: Config 761 (Ressio Software - 770), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 761 (Ressio Software - 770)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 1370\n Properties:\n 📝\u0000 hubspot_owner_id: 471 changes\n 📝\u0000 domain: 199 changes\n 📝\u0000 name: 202 changes\n 📝\u0000 country: 163 changes\n 📝\u0000 industry: 170 changes\n 📝\u0000 phone: 165 changes\n\n 🔔\u0000 Event: creation\n Count: 207\n\n 🔔\u0000 Event: association_change\n Count: 435\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 1582\n Properties:\n 📝\u0000 hubspot_owner_id: 476 changes\n 📝\u0000 phone: 155 changes\n 📝\u0000 mobilephone: 97 changes\n 📝\u0000 lastname: 153 changes\n 📝\u0000 email: 109 changes\n 📝\u0000 associatedcompanyid: 160 changes\n 📝\u0000 firstname: 163 changes\n 📝\u0000 jobtitle: 139 changes\n 📝\u0000 country: 130 changes\n\n 🔔\u0000 Event: creation\n Count: 152\n\n 🔔\u0000 Event: association_change\n Count: 386\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 119\n Properties:\n 📝\u0000 closedate: 16 changes\n 📝\u0000 dealstage: 36 changes\n 📝\u0000 hs_deal_stage_probability: 53 changes\n 📝\u0000 hubspot_owner_id: 9 changes\n 📝\u0000 amount: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 17\n\n 🔔\u0000 Event: association_change\n Count: 119\n\n\n INFO Looking for metrics: Config 537 (Mobiz - 563), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 537 (Mobiz - 563)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 19\n\n 🔔\u0000 Event: property_change\n Count: 751\n Properties:\n 📝\u0000 email: 26 changes\n 📝\u0000 lastname: 18 changes\n 📝\u0000 phone: 15 changes\n 📝\u0000 hubspot_owner_id: 630 changes\n 📝\u0000 firstname: 21 changes\n 📝\u0000 country: 9 changes\n 📝\u0000 jobtitle: 14 changes\n 📝\u0000 associatedcompanyid: 15 changes\n 📝\u0000 mobilephone: 3 changes\n\n 🔔\u0000 Event: association_change\n Count: 35\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 36\n\n 🔔\u0000 Event: property_change\n Count: 24\n Properties:\n 📝\u0000 hubspot_owner_id: 6 changes\n 📝\u0000 domain: 5 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 industry: 4 changes\n 📝\u0000 name: 4 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 1\n Properties:\n 📝\u0000 hs_deal_stage_probability: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: creation\n Count: 1\n\n\n INFO Looking for metrics: Config 428 (Welcome to the Jungle UK - 461), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 428 (Welcome to the Jungle UK - 461)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 107\n\n 🔔\u0000 Event: property_change\n Count: 352\n Properties:\n 📝\u0000 firstname: 34 changes\n 📝\u0000 email: 35 changes\n 📝\u0000 lastname: 32 changes\n 📝\u0000 associatedcompanyid: 37 changes\n 📝\u0000 country: 29 changes\n 📝\u0000 jobtitle: 29 changes\n 📝\u0000 phone: 43 changes\n 📝\u0000 hubspot_owner_id: 108 changes\n 📝\u0000 mobilephone: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 32\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 225\n Properties:\n 📝\u0000 hs_deal_stage_probability: 53 changes\n 📝\u0000 product: 7 changes\n 📝\u0000 closedate: 20 changes\n 📝\u0000 dealstage: 40 changes\n 📝\u0000 hs_manual_forecast_category: 31 changes\n 📝\u0000 amount: 26 changes\n 📝\u0000 deal_currency_code: 17 changes\n 📝\u0000 dealname: 5 changes\n 📝\u0000 segment: 16 changes\n 📝\u0000 hs_next_step: 6 changes\n 📝\u0000 deal_source: 4 changes\n\n 🔔\u0000 Event: creation\n Count: 14\n\n 🔔\u0000 Event: association_change\n Count: 41\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 34\n Properties:\n 📝\u0000 hubspot_owner_id: 18 changes\n 📝\u0000 domain: 9 changes\n 📝\u0000 name: 7 changes\n\n 🔔\u0000 Event: creation\n Count: 9\n\n 🔔\u0000 Event: association_change\n Count: 130\n\n\n INFO Looking for metrics: Config 581 (Penfold - 606), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 581 (Penfold - 606)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 161\n\n 🔔\u0000 Event: creation\n Count: 117\n\n 🔔\u0000 Event: property_change\n Count: 574\n Properties:\n 📝\u0000 email: 117 changes\n 📝\u0000 lastname: 69 changes\n 📝\u0000 firstname: 74 changes\n 📝\u0000 associatedcompanyid: 74 changes\n 📝\u0000 hubspot_owner_id: 156 changes\n 📝\u0000 jobtitle: 28 changes\n 📝\u0000 country: 22 changes\n 📝\u0000 phone: 18 changes\n 📝\u0000 mobilephone: 16 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 21\n\n 🔔\u0000 Event: property_change\n Count: 78\n Properties:\n 📝\u0000 domain: 22 changes\n 📝\u0000 name: 23 changes\n 📝\u0000 hubspot_owner_id: 27 changes\n 📝\u0000 phone: 3 changes\n 📝\u0000 industry: 1 changes\n 📝\u0000 country: 2 changes\n\n 🔔\u0000 Event: association_change\n Count: 167\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 18\n\n 🔔\u0000 Event: creation\n Count: 6\n\n 🔔\u0000 Event: property_change\n Count: 49\n Properties:\n 📝\u0000 dealstage: 13 changes\n 📝\u0000 hs_deal_stage_probability: 18 changes\n 📝\u0000 hs_manual_forecast_category: 10 changes\n 📝\u0000 hubspot_owner_id: 2 changes\n 📝\u0000 hs_next_step: 1 changes\n 📝\u0000 amount: 2 changes\n 📝\u0000 closedate: 3 changes\n\n\n INFO Looking for metrics: Config 1015 (Travefy - 1049), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1015 (Travefy - 1049)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 1064\n Properties:\n 📝\u0000 firstname: 194 changes\n 📝\u0000 phone: 141 changes\n 📝\u0000 email: 156 changes\n 📝\u0000 lastname: 171 changes\n 📝\u0000 associatedcompanyid: 156 changes\n 📝\u0000 hubspot_owner_id: 228 changes\n 📝\u0000 jobtitle: 11 changes\n 📝\u0000 mobilephone: 5 changes\n 📝\u0000 country: 2 changes\n\n 🔔\u0000 Event: creation\n Count: 154\n\n 🔔\u0000 Event: association_change\n Count: 411\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 1072\n Properties:\n 📝\u0000 deal_currency_code: 98 changes\n 📝\u0000 amount: 108 changes\n 📝\u0000 hs_deal_stage_probability: 262 changes\n 📝\u0000 hs_manual_forecast_category: 217 changes\n 📝\u0000 hubspot_owner_id: 107 changes\n 📝\u0000 closedate: 109 changes\n 📝\u0000 dealstage: 163 changes\n 📝\u0000 seats_interest: 2 changes\n 📝\u0000 pipeline: 5 changes\n 📝\u0000 competitor_deal: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 99\n\n 🔔\u0000 Event: association_change\n Count: 297\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 151\n\n 🔔\u0000 Event: association_change\n Count: 510\n\n 🔔\u0000 Event: property_change\n Count: 263\n Properties:\n 📝\u0000 name: 171 changes\n 📝\u0000 hubspot_owner_id: 92 changes\n\n\n INFO Looking for metrics: Config 413 (VCC - 347), Date 2026-04-16.","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.27027926,"top":1.0,"width":0.058843084,"height":-0.042298436},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.27227393,"top":1.0,"width":0.005319149,"height":-0.04549086},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.32912233,"top":1.0,"width":0.058843084,"height":-0.042298436},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33111703,"top":1.0,"width":0.005319149,"height":-0.04549086},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.3879654,"top":1.0,"width":0.058843084,"height":-0.042298436},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.3899601,"top":1.0,"width":0.005319149,"height":-0.04549086},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Build full day activity summary from Screenpipe (claude)","depth":2,"bounds":{"left":0.44680852,"top":1.0,"width":0.058843084,"height":-0.042298436},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.4488032,"top":1.0,"width":0.005319149,"height":-0.04549086},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.5056516,"top":1.0,"width":0.058843084,"height":-0.042298436},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.50764626,"top":1.0,"width":0.005319149,"height":-0.04549086},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.56449467,"top":1.0,"width":0.058843084,"height":-0.042298436},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.56648934,"top":1.0,"width":0.005319149,"height":-0.04549086},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.62333775,"top":1.0,"width":0.058843084,"height":-0.042298436},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.6253325,"top":1.0,"width":0.005319149,"height":-0.04549086},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"ec2-user@ip-10-30-159-186:~ (nc)","depth":2,"bounds":{"left":0.6821808,"top":1.0,"width":0.058843084,"height":-0.042298436},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.68417555,"top":1.0,"width":0.005319149,"height":-0.04549086},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.7273936,"top":1.0,"width":0.01861702,"height":-0.023144484},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"ec2-user@ip-10-30-159-186:~","depth":1,"bounds":{"left":0.47539893,"top":1.0,"width":0.068484046,"height":-0.02394259},"role_description":"text"}]...
|
4495648462221987536
|
7903532624451610999
|
idle
|
accessibility
|
NULL
|
Properties:
📝 hubspot_owner_id: 2837 Properties:
📝 hubspot_owner_id: 2837 changes
📝 email: 4471 changes
📝 phone: 3661 changes
📝 lastname: 16150 changes
📝 firstname: 22622 changes
📝 associatedcompanyid: 4860 changes
📝 mobilephone: 13 changes
📝 jobtitle: 5 changes
📝 country: 1 changes
🔔 Event: creation
Count: 4471
🔔 Event: association_change
Count: 17648
📦 Object Type: deal
🔔 Event: creation
Count: 7588
🔔 Event: property_change
Count: 82720
Properties:
📝 dealname: 4204 changes
📝 amount: 22594 changes
📝 hs_deal_stage_probability: 17646 changes
📝 pipeline: 4356 changes
📝 dealstage: 14626 changes
📝 closedate: 11096 changes
📝 hubspot_owner_id: 6094 changes
📝 selected_date: 1969 changes
📝 deal_currency_code: 135 changes
🔔 Event: association_change
Count: 22773
INFO Looking for metrics: Config 878 (Dingus and Zazzy - 929), Date 2026-04-16.
📊 Webhook Metrics for Config 878 (Dingus and Zazzy - 929)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: property_change
Count: 158
Properties:
📝 amount_in_home_currency: 24 changes
📝 hs_deal_stage_probability: 35 changes
📝 hs_manual_forecast_category: 27 changes
📝 closedate: 23 changes
📝 lost_reason: 9 changes
📝 dealstage: 27 changes
📝 dealname: 9 changes
📝 hubspot_owner_id: 2 changes
📝 source_attribution: 2 changes
🔔 Event: creation
Count: 18
🔔 Event: association_change
Count: 52
📦 Object Type: company
🔔 Event: property_change
Count: 59
Properties:
📝 domain: 14 changes
📝 name: 14 changes
📝 hubspot_owner_id: 17 changes
📝 industry: 5 changes
📝 phone: 4 changes
📝 country: 5 changes
🔔 Event: association_change
Count: 69
🔔 Event: creation
Count: 14
📦 Object Type: contact
🔔 Event: property_change
Count: 192
Properties:
📝 phone: 8 changes
📝 lastname: 22 changes
📝 firstname: 22 changes
📝 jobtitle: 20 changes
📝 hubspot_owner_id: 76 changes
📝 email: 17 changes
📝 associatedcompanyid: 16 changes
📝 mobilephone: 11 changes
🔔 Event: association_change
Count: 53
🔔 Event: creation
Count: 20
INFO Looking for metrics: Config 671 (CosmosID - 691), Date 2026-04-16.
📊 Webhook Metrics for Config 671 (CosmosID - 691)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: creation
Count: 5
🔔 Event: property_change
Count: 15
Properties:
📝 hubspot_owner_id: 6 changes
📝 name: 5 changes
📝 domain: 3 changes
📝 phone: 1 changes
🔔 Event: association_change
Count: 44
📦 Object Type: deal
🔔 Event: property_change
Count: 78
Properties:
📝 hs_deal_stage_probability: 14 changes
📝 createdate: 10 changes
📝 hs_manual_forecast_category: 13 changes
📝 deal_currency_code: 4 changes
📝 amount: 20 changes
📝 closedate: 8 changes
📝 dealstage: 4 changes
📝 dealname: 3 changes
📝 dealtype: 1 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: association_change
Count: 30
🔔 Event: creation
Count: 10
📦 Object Type: contact
🔔 Event: creation
Count: 13
🔔 Event: property_change
Count: 145
Properties:
📝 jobtitle: 77 changes
📝 hubspot_owner_id: 12 changes
📝 firstname: 9 changes
📝 phone: 5 changes
📝 lastname: 8 changes
📝 email: 13 changes
📝 associatedcompanyid: 12 changes
📝 country: 9 changes
🔔 Event: association_change
Count: 34
INFO Looking for metrics: Config 652 (Abode - 673), Date 2026-04-16.
📊 Webhook Metrics for Config 652 (Abode - 673)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 1
Properties:
📝 country: 1 changes
🔔 Event: association_change
Count: 10
📦 Object Type: contact
🔔 Event: creation
Count: 6
🔔 Event: property_change
Count: 40
Properties:
📝 phone: 3 changes
📝 hubspot_owner_id: 8 changes
📝 email: 6 changes
📝 associatedcompanyid: 4 changes
📝 country: 4 changes
📝 lastname: 5 changes
📝 jobtitle: 4 changes
📝 firstname: 5 changes
📝 mobilephone: 1 changes
🔔 Event: association_change
Count: 9
📦 Object Type: deal
🔔 Event: creation
Count: 1
🔔 Event: association_change
Count: 3
🔔 Event: property_change
Count: 13
Properties:
📝 dealstage: 4 changes
📝 hs_deal_stage_probability: 5 changes
📝 dealname: 1 changes
📝 closedate: 1 changes
📝 hs_manual_forecast_category: 1 changes
📝 amount: 1 changes
INFO Looking for metrics: Config 1049 (Classavo - 851), Date 2026-04-16.
📊 Webhook Metrics for Config 1049 (Classavo - 851)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: association_change
Count: 2
📦 Object Type: contact
🔔 Event: association_change
Count: 1
🔔 Event: property_change
Count: 3
Properties:
📝 firstname: 1 changes
📝 lastname: 1 changes
📝 jobtitle: 1 changes
📦 Object Type: deal
🔔 Event: association_change
Count: 3
🔔 Event: property_change
Count: 14
Properties:
📝 dealstage: 4 changes
📝 hs_deal_stage_probability: 5 changes
📝 closedate: 3 changes
📝 deal_currency_code: 1 changes
📝 amount: 1 changes
🔔 Event: creation
Count: 1
INFO Looking for metrics: Config 290 (D1 Training - 308), Date 2026-04-16.
📊 Webhook Metrics for Config 290 (D1 Training - 308)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 5
Properties:
📝 name: 4 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: creation
Count: 1
🔔 Event: association_change
Count: 6
📦 Object Type: deal
🔔 Event: association_change
Count: 40
🔔 Event: creation
Count: 35
🔔 Event: property_change
Count: 405
Properties:
📝 hs_deal_stage_probability: 149 changes
📝 dealtype: 34 changes
📝 dealstage: 114 changes
📝 hubspot_owner_id: 5 changes
📝 closedate: 97 changes
📝 dealname: 3 changes
📝 pipeline: 2 changes
📝 hs_manual_forecast_category: 1 changes
📦 Object Type: contact
🔔 Event: creation
Count: 50
🔔 Event: property_change
Count: 314
Properties:
📝 lastname: 53 changes
📝 email: 46 changes
📝 firstname: 58 changes
📝 mobilephone: 26 changes
📝 phone: 70 changes
📝 hubspot_owner_id: 59 changes
📝 associatedcompanyid: 2 changes
🔔 Event: association_change
Count: 42
INFO Looking for metrics: Config 1019 (SimpleConsign - 1088), Date 2026-04-16.
📊 Webhook Metrics for Config 1019 (SimpleConsign - 1088)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 692
🔔 Event: property_change
Count: 2659
Properties:
📝 email: 334 changes
📝 hubspot_owner_id: 980 changes
📝 associatedcompanyid: 327 changes
📝 lastname: 332 changes
📝 phone: 20 changes
📝 firstname: 333 changes
📝 jobtitle: 321 changes
📝 mobilephone: 7 changes
📝 country: 5 changes
🔔 Event: creation
Count: 335
📦 Object Type: company
🔔 Event: association_change
Count: 698
🔔 Event: property_change
Count: 637
Properties:
📝 domain: 310 changes
📝 name: 312 changes
📝 phone: 5 changes
📝 hubspot_owner_id: 4 changes
📝 country: 3 changes
📝 industry: 3 changes
🔔 Event: creation
Count: 312
📦 Object Type: deal
🔔 Event: association_change
Count: 46
🔔 Event: creation
Count: 9
INFO Looking for metrics: Config 311 (Lemon.io - 329), Date 2026-04-16.
📊 Webhook Metrics for Config 311 (Lemon.io - 329)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 128
Properties:
📝 domain: 26 changes
📝 name: 24 changes
📝 industry: 18 changes
📝 country: 17 changes
📝 hubspot_owner_id: 32 changes
📝 phone: 11 changes
🔔 Event: creation
Count: 26
🔔 Event: association_change
Count: 120
📦 Object Type: deal
🔔 Event: creation
Count: 16
🔔 Event: property_change
Count: 385
Properties:
📝 closedate: 21 changes
📝 dealstage: 49 changes
📝 days_to_close: 19 changes
📝 hs_deal_stage_probability: 58 changes
📝 hs_manual_forecast_category: 34 changes
📝 hs_next_step: 14 changes
📝 level_of_involvement: 15 changes
📝 hubspot_owner_id: 20 changes
📝 freelancers_under_review: 31 changes
📝 pipeline: 8 changes
📝 dealname: 30 changes
📝 deal_currency_code: 8 changes
📝 amount: 13 changes
📝 hs_priority: 8 changes
📝 budget_limitations: 6 changes
📝 need: 7 changes
📝 positive_outcome: 6 changes
📝 competition: 6 changes
📝 negative_consequences: 6 changes
📝 selection_process: 8 changes
📝 project_team_structure: 7 changes
📝 champion: 6 changes
📝 red_flags: 5 changes
🔔 Event: association_change
Count: 63
📦 Object Type: contact
🔔 Event: property_change
Count: 336
Properties:
📝 hubspot_owner_id: 74 changes
📝 email: 56 changes
📝 firstname: 52 changes
📝 lastname: 48 changes
📝 country: 21 changes
📝 jobtitle: 26 changes
📝 associatedcompanyid: 39 changes
📝 phone: 15 changes
📝 mobilephone: 5 changes
🔔 Event: association_change
Count: 103
🔔 Event: creation
Count: 54
INFO Looking for metrics: Config 802 (Street Group - 853), Date 2026-04-16.
📊 Webhook Metrics for Config 802 (Street Group - 853)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: creation
Count: 252
🔔 Event: association_change
Count: 558
🔔 Event: property_change
Count: 1830
Properties:
📝 phone: 170 changes
📝 lastname: 306 changes
📝 firstname: 318 changes
📝 email: 297 changes
📝 country: 287 changes
📝 jobtitle: 208 changes
📝 associatedcompanyid: 180 changes
📝 hubspot_owner_id: 52 changes
📝 mobilephone: 12 changes
📦 Object Type: company
🔔 Event: association_change
Count: 617
🔔 Event: creation
Count: 31
🔔 Event: property_change
Count: 94
Properties:
📝 industry: 6 changes
📝 country: 10 changes
📝 phone: 10 changes
📝 name: 22 changes
📝 domain: 35 changes
📝 hubspot_owner_id: 11 changes
📦 Object Type: deal
🔔 Event: association_change
Count: 121
🔔 Event: property_change
Count: 338
Properties:
📝 hs_deal_stage_probability: 78 changes
📝 deal_currency_code: 22 changes
📝 amount: 36 changes
📝 closedate: 42 changes
📝 dealstage: 91 changes
📝 dealtype: 2 changes
📝 hs_manual_forecast_category: 28 changes
📝 dealname: 8 changes
📝 hs_forecast_probability: 16 changes
📝 invoice_start_date: 8 changes
📝 hubspot_owner_id: 7 changes
🔔 Event: creation
Count: 23
INFO Looking for metrics: Config 1053 (Sensi.AI - 1117), Date 2026-04-16.
📊 Webhook Metrics for Config 1053 (Sensi.AI - 1117)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 156
Properties:
📝 domain: 32 changes
📝 phone: 23 changes
📝 industry: 27 changes
📝 country: 29 changes
📝 name: 39 changes
📝 hubspot_owner_id: 6 changes
🔔 Event: creation
Count: 39
🔔 Event: association_change
Count: 1499
📦 Object Type: deal
🔔 Event: property_change
Count: 491
Properties:
📝 hs_deal_stage_probability: 96 changes
📝 hs_next_step: 46 changes
📝 next_steps_jiminny: 38 changes
📝 hubspot_owner_id: 17 changes
📝 monthly_billable_hours: 10 changes
📝 closedate: 18 changes
📝 amount: 119 changes
📝 deal_currency_code: 115 changes
📝 dealstage: 23 changes
📝 dealname: 4 changes
📝 client_segmentation_jiminny: 1 changes
📝 pain_points__main_objections: 1 changes
📝 product_feedback: 1 changes
📝 red_flags: 1 changes
📝 competitors: 1 changes
🔔 Event: association_change
Count: 98
🔔 Event: creation
Count: 74
📦 Object Type: contact
🔔 Event: association_change
Count: 1577
🔔 Event: property_change
Count: 3768
Properties:
📝 email: 857 changes
📝 mobilephone: 104 changes
📝 lastname: 854 changes
📝 jobtitle: 98 changes
📝 phone: 130 changes
📝 firstname: 870 changes
📝 hubspot_owner_id: 92 changes
📝 associatedcompanyid: 737 changes
📝 country: 26 changes
🔔 Event: creation
Count: 872
INFO Looking for metrics: Config 87 (Repsly - 93), Date 2026-04-16.
📊 Webhook Metrics for Config 87 (Repsly - 93)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 295
🔔 Event: property_change
Count: 1285
Properties:
📝 lastname: 150 changes
📝 email: 128 changes
📝 firstname: 162 changes
📝 associatedcompanyid: 145 changes
📝 country: 130 changes
📝 hubspot_owner_id: 175 changes
📝 mobilephone: 203 changes
📝 phone: 60 changes
📝 jobtitle: 132 changes
🔔 Event: creation
Count: 159
📦 Object Type: deal
🔔 Event: creation
Count: 3
🔔 Event: association_change
Count: 13
🔔 Event: property_change
Count: 23
Properties:
📝 hs_deal_stage_probability: 4 changes
📝 amount: 14 changes
📝 hubspot_owner_id: 1 changes
📝 closedate: 2 changes
📝 hs_next_step: 1 changes
📝 dealstage: 1 changes
📦 Object Type: company
🔔 Event: association_change
Count: 298
🔔 Event: property_change
Count: 23
Properties:
📝 domain: 9 changes
📝 country: 4 changes
📝 name: 6 changes
📝 hubspot_owner_id: 3 changes
📝 phone: 1 changes
🔔 Event: creation
Count: 9
INFO Looking for metrics: Config 518 (Prolific - 544), Date 2026-04-16.
📊 Webhook Metrics for Config 518 (Prolific - 544)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: property_change
Count: 14
Properties:
📝 amount: 3 changes
📝 hs_deal_stage_probability: 4 changes
📝 dealname: 3 changes
📝 closedate: 3 changes
📝 dealstage: 1 changes
🔔 Event: creation
Count: 3
🔔 Event: association_change
Count: 9
📦 Object Type: contact
🔔 Event: creation
Count: 534
🔔 Event: property_change
Count: 7533
Properties:
📝 jobtitle: 73 changes
📝 phone: 2 changes
📝 email: 561 changes
📝 lastname: 81 changes
📝 firstname: 90 changes
📝 associatedcompanyid: 166 changes
📝 hubspot_owner_id: 6151 changes
📝 country: 409 changes
🔔 Event: association_change
Count: 349
📦 Object Type: company
🔔 Event: creation
Count: 30
🔔 Event: association_change
Count: 352
🔔 Event: property_change
Count: 575
Properties:
📝 domain: 30 changes
📝 country: 421 changes
📝 phone: 12 changes
📝 industry: 18 changes
📝 name: 23 changes
📝 hubspot_owner_id: 71 changes
INFO Looking for metrics: Config 761 (Ressio Software - 770), Date 2026-04-16.
📊 Webhook Metrics for Config 761 (Ressio Software - 770)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 1370
Properties:
📝 hubspot_owner_id: 471 changes
📝 domain: 199 changes
📝 name: 202 changes
📝 country: 163 changes
📝 industry: 170 changes
📝 phone: 165 changes
🔔 Event: creation
Count: 207
🔔 Event: association_change
Count: 435
📦 Object Type: contact
🔔 Event: property_change
Count: 1582
Properties:
📝 hubspot_owner_id: 476 changes
📝 phone: 155 changes
📝 mobilephone: 97 changes
📝 lastname: 153 changes
📝 email: 109 changes
📝 associatedcompanyid: 160 changes
📝 firstname: 163 changes
📝 jobtitle: 139 changes
📝 country: 130 changes
🔔 Event: creation
Count: 152
🔔 Event: association_change
Count: 386
📦 Object Type: deal
🔔 Event: property_change
Count: 119
Properties:
📝 closedate: 16 changes
📝 dealstage: 36 changes
📝 hs_deal_stage_probability: 53 changes
📝 hubspot_owner_id: 9 changes
📝 amount: 5 changes
🔔 Event: creation
Count: 17
🔔 Event: association_change
Count: 119
INFO Looking for metrics: Config 537 (Mobiz - 563), Date 2026-04-16.
📊 Webhook Metrics for Config 537 (Mobiz - 563)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: creation
Count: 19
🔔 Event: property_change
Count: 751
Properties:
📝 email: 26 changes
📝 lastname: 18 changes
📝 phone: 15 changes
📝 hubspot_owner_id: 630 changes
📝 firstname: 21 changes
📝 country: 9 changes
📝 jobtitle: 14 changes
📝 associatedcompanyid: 15 changes
📝 mobilephone: 3 changes
🔔 Event: association_change
Count: 35
📦 Object Type: company
🔔 Event: association_change
Count: 36
🔔 Event: property_change
Count: 24
Properties:
📝 hubspot_owner_id: 6 changes
📝 domain: 5 changes
📝 country: 4 changes
📝 industry: 4 changes
📝 name: 4 changes
📝 phone: 1 changes
🔔 Event: creation
Count: 4
📦 Object Type: deal
🔔 Event: property_change
Count: 1
Properties:
📝 hs_deal_stage_probability: 1 changes
🔔 Event: association_change
Count: 3
🔔 Event: creation
Count: 1
INFO Looking for metrics: Config 428 (Welcome to the Jungle UK - 461), Date 2026-04-16.
📊 Webhook Metrics for Config 428 (Welcome to the Jungle UK - 461)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 107
🔔 Event: property_change
Count: 352
Properties:
📝 firstname: 34 changes
📝 email: 35 changes
📝 lastname: 32 changes
📝 associatedcompanyid: 37 changes
📝 country: 29 changes
📝 jobtitle: 29 changes
📝 phone: 43 changes
📝 hubspot_owner_id: 108 changes
📝 mobilephone: 5 changes
🔔 Event: creation
Count: 32
📦 Object Type: deal
🔔 Event: property_change
Count: 225
Properties:
📝 hs_deal_stage_probability: 53 changes
📝 product: 7 changes
📝 closedate: 20 changes
📝 dealstage: 40 changes
📝 hs_manual_forecast_category: 31 changes
📝 amount: 26 changes
📝 deal_currency_code: 17 changes
📝 dealname: 5 changes
📝 segment: 16 changes
📝 hs_next_step: 6 changes
📝 deal_source: 4 changes
🔔 Event: creation
Count: 14
🔔 Event: association_change
Count: 41
📦 Object Type: company
🔔 Event: property_change
Count: 34
Properties:
📝 hubspot_owner_id: 18 changes
📝 domain: 9 changes
📝 name: 7 changes
🔔 Event: creation
Count: 9
🔔 Event: association_change
Count: 130
INFO Looking for metrics: Config 581 (Penfold - 606), Date 2026-04-16.
📊 Webhook Metrics for Config 581 (Penfold - 606)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 161
🔔 Event: creation
Count: 117
🔔 Event: property_change
Count: 574
Properties:
📝 email: 117 changes
📝 lastname: 69 changes
📝 firstname: 74 changes
📝 associatedcompanyid: 74 changes
📝 hubspot_owner_id: 156 changes
📝 jobtitle: 28 changes
📝 country: 22 changes
📝 phone: 18 changes
📝 mobilephone: 16 changes
📦 Object Type: company
🔔 Event: creation
Count: 21
🔔 Event: property_change
Count: 78
Properties:
📝 domain: 22 changes
📝 name: 23 changes
📝 hubspot_owner_id: 27 changes
📝 phone: 3 changes
📝 industry: 1 changes
📝 country: 2 changes
🔔 Event: association_change
Count: 167
📦 Object Type: deal
🔔 Event: association_change
Count: 18
🔔 Event: creation
Count: 6
🔔 Event: property_change
Count: 49
Properties:
📝 dealstage: 13 changes
📝 hs_deal_stage_probability: 18 changes
📝 hs_manual_forecast_category: 10 changes
📝 hubspot_owner_id: 2 changes
📝 hs_next_step: 1 changes
📝 amount: 2 changes
📝 closedate: 3 changes
INFO Looking for metrics: Config 1015 (Travefy - 1049), Date 2026-04-16.
📊 Webhook Metrics for Config 1015 (Travefy - 1049)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: property_change
Count: 1064
Properties:
📝 firstname: 194 changes
📝 phone: 141 changes
📝 email: 156 changes
📝 lastname: 171 changes
📝 associatedcompanyid: 156 changes
📝 hubspot_owner_id: 228 changes
📝 jobtitle: 11 changes
📝 mobilephone: 5 changes
📝 country: 2 changes
🔔 Event: creation
Count: 154
🔔 Event: association_change
Count: 411
📦 Object Type: deal
🔔 Event: property_change
Count: 1072
Properties:
📝 deal_currency_code: 98 changes
📝 amount: 108 changes
📝 hs_deal_stage_probability: 262 changes
📝 hs_manual_forecast_category: 217 changes
📝 hubspot_owner_id: 107 changes
📝 closedate: 109 changes
📝 dealstage: 163 changes
📝 seats_interest: 2 changes
📝 pipeline: 5 changes
📝 competitor_deal: 1 changes
🔔 Event: creation
Count: 99
🔔 Event: association_change
Count: 297
📦 Object Type: company
🔔 Event: creation
Count: 151
🔔 Event: association_change
Count: 510
🔔 Event: property_change
Count: 263
Properties:
📝 name: 171 changes
📝 hubspot_owner_id: 92 changes
INFO Looking for metrics: Config 413 (VCC - 347), Date 2026-04-16.
DOCKER
Close Tab
-zsh
Close Tab
-zsh
Close Tab
✳ Build full day activity summary from Screenpipe (claude)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
APP (-zsh)
Close Tab
ec2-user@ip-10-30-159-186:~ (nc)
Close Tab
⌥⌘1
ec2-user@ip-10-30-159-186:~...
|
69501
|
|
72879
|
1779
|
15
|
2026-04-23T06:20:59.611702+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925259611_m2.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Re
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
27/36
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class
Slack
Teams...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.7972075,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"bounds":{"left":0.8125,"top":0.019952115,"width":0.10305851,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.35239363,"top":0.15482841,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.3650266,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Re","depth":4,"bounds":{"left":0.37599733,"top":0.15403032,"width":0.051861703,"height":0.015961692},"value":"Re","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.4368351,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.44680852,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.4554521,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.46409574,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"27/36","depth":4,"bounds":{"left":0.47772607,"top":0.15323225,"width":0.025598405,"height":0.017557861},"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.50332445,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.5119681,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.5206117,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.52925533,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.6918218,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"13","depth":4,"bounds":{"left":0.66921544,"top":0.18355946,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.68085104,"top":0.18355946,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.69049203,"top":0.1819633,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.6978058,"top":0.1819633,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealContactService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsightsCriteriaBuilder.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepositoryInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsServiceRepositories.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PerformanceMonitor.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodOptionDecoratorInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodService.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskTypes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisk.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GroupDealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Configuration","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Traits","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"UpdateOpportunitySpecifications.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dev","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dialers","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DTOs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Livestream","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Migrate","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackThemes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playbooks","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playlists","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Postmark","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Reports","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRetentionPolicyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsSendCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateMockAskJiminnyReportResultCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DeleteReportCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"GenerateMarketingReport.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Team.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Usage.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Slack","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Teams","depth":10,"role_description":"text"}]...
|
-9000225036277920761
|
7902282280547713340
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Re
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
27/36
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class
Slack
Teams...
|
72877
|
|
72880
|
1778
|
12
|
2026-04-23T06:21:08.339347+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925268339_m1.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
9/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Reposit","depth":4,"value":"Reposit","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9/10","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"13","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealContactService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsightsCriteriaBuilder.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepositoryInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsServiceRepositories.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PerformanceMonitor.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodOptionDecoratorInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodService.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskTypes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisk.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GroupDealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Configuration","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Traits","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"UpdateOpportunitySpecifications.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dev","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dialers","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DTOs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Livestream","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Migrate","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackThemes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playbooks","depth":10,"role_description":"text"}]...
|
6117205323764006380
|
7902282280547189052
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
9/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks...
|
NULL
|
|
72881
|
1779
|
16
|
2026-04-23T06:21:08.860540+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925268860_m2.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
"Reposit" not found, press ⌘G to search fr "Reposit" not found, press ⌘G to search from the top
text/html
text/html
text/html
Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
10/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class
Slack
Teams
Tracks
Transcription...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"\"Reposit\" not found, press ⌘G to search from the top","depth":2,"bounds":{"left":0.52293885,"top":0.8739026,"width":0.109042555,"height":0.013567438},"value":"\"Reposit\" not found, press ⌘G to search from the top","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"bounds":{"left":0.52293885,"top":0.8739026,"width":0.109042555,"height":0.013567438},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":3,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.7972075,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"bounds":{"left":0.8125,"top":0.019952115,"width":0.10305851,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.35239363,"top":0.15482841,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.3650266,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Reposit","depth":4,"bounds":{"left":0.37599733,"top":0.15403032,"width":0.051861703,"height":0.015961692},"value":"Reposit","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.4368351,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.44680852,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.4554521,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.46409574,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"10/10","depth":4,"bounds":{"left":0.47772607,"top":0.15323225,"width":0.025598405,"height":0.017557861},"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.50332445,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.5119681,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.5206117,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.52925533,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.6918218,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"13","depth":4,"bounds":{"left":0.66921544,"top":0.18355946,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.68085104,"top":0.18355946,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.69049203,"top":0.1819633,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.6978058,"top":0.1819633,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealContactService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsightsCriteriaBuilder.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepositoryInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsServiceRepositories.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PerformanceMonitor.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodOptionDecoratorInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodService.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskTypes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisk.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GroupDealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Configuration","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Traits","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"UpdateOpportunitySpecifications.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dev","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dialers","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DTOs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Livestream","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Migrate","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackThemes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playbooks","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playlists","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Postmark","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Reports","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRetentionPolicyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsSendCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateMockAskJiminnyReportResultCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DeleteReportCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"GenerateMarketingReport.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Team.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Usage.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Slack","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Teams","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Tracks","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Transcription","depth":10,"role_description":"text"}]...
|
-2965016558402427052
|
7902282280547189052
|
click
|
accessibility
|
NULL
|
"Reposit" not found, press ⌘G to search fr "Reposit" not found, press ⌘G to search from the top
text/html
text/html
text/html
Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
10/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class
Slack
Teams
Tracks
Transcription...
|
NULL
|
|
72889
|
1779
|
20
|
2026-04-23T06:21:22.077823+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925282077_m2.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
5/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class
Slack
Teams...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.7972075,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"bounds":{"left":0.8125,"top":0.019952115,"width":0.10305851,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.35239363,"top":0.15482841,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.3650266,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Reposit","depth":4,"bounds":{"left":0.37599733,"top":0.15403032,"width":0.051861703,"height":0.015961692},"value":"Reposit","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.4368351,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.44680852,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.4554521,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.46409574,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5/10","depth":4,"bounds":{"left":0.47772607,"top":0.15323225,"width":0.025598405,"height":0.017557861},"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.50332445,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.5119681,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.5206117,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.52925533,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.6918218,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"13","depth":4,"bounds":{"left":0.66921544,"top":0.18355946,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.68085104,"top":0.18355946,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.69049203,"top":0.1819633,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.6978058,"top":0.1819633,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealContactService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsightsCriteriaBuilder.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepositoryInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsServiceRepositories.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PerformanceMonitor.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodOptionDecoratorInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodService.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskTypes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisk.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GroupDealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Configuration","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Traits","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"UpdateOpportunitySpecifications.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dev","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dialers","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DTOs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Livestream","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Migrate","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackThemes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playbooks","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playlists","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Postmark","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Reports","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRetentionPolicyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsSendCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateMockAskJiminnyReportResultCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DeleteReportCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"GenerateMarketingReport.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Team.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Usage.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Slack","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Teams","depth":10,"role_description":"text"}]...
|
2770864002192739781
|
7902282280547189052
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
5/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class
Slack
Teams...
|
NULL
|
|
72890
|
1779
|
21
|
2026-04-23T06:21:27.358449+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925287358_m2.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
6/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.7972075,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"bounds":{"left":0.8125,"top":0.019952115,"width":0.10305851,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.35239363,"top":0.15482841,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.3650266,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Reposit","depth":4,"bounds":{"left":0.37599733,"top":0.15403032,"width":0.051861703,"height":0.015961692},"value":"Reposit","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.4368351,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.44680852,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.4554521,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.46409574,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6/10","depth":4,"bounds":{"left":0.47772607,"top":0.15323225,"width":0.025598405,"height":0.017557861},"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.50332445,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.5119681,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.5206117,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.52925533,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.6918218,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"13","depth":4,"bounds":{"left":0.66921544,"top":0.18355946,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.68085104,"top":0.18355946,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.69049203,"top":0.1819633,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.6978058,"top":0.1819633,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealContactService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsightsCriteriaBuilder.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepositoryInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsServiceRepositories.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PerformanceMonitor.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodOptionDecoratorInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodService.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskTypes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisk.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GroupDealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Configuration","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Traits","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"UpdateOpportunitySpecifications.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dev","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dialers","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DTOs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Livestream","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Migrate","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackThemes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playbooks","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playlists","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Postmark","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Reports","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRetentionPolicyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsSendCommand.php, class","depth":11,"role_description":"text"}]...
|
-1485654039975727576
|
7902282280547189052
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
6/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class...
|
72889
|
|
72900
|
1778
|
21
|
2026-04-23T06:21:51.648860+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925311648_m1.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
6/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Reposit","depth":4,"value":"Reposit","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6/10","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"13","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealContactService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsightsCriteriaBuilder.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepositoryInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsServiceRepositories.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PerformanceMonitor.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodOptionDecoratorInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodService.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskTypes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisk.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GroupDealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Configuration","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Traits","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"UpdateOpportunitySpecifications.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dev","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dialers","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DTOs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Livestream","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Migrate","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackThemes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playbooks","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playlists","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Postmark","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Reports","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRetentionPolicyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsSendCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateMockAskJiminnyReportResultCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DeleteReportCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"GenerateMarketingReport.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Team.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Usage.php, class","depth":11,"role_description":"text"}]...
|
8952070983622228564
|
7902282280547189052
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
6/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
Migrate
PlaybackThemes
Playbooks
Playlists
Postmark
ProphetAi
Reports
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class...
|
72897
|
|
72893
|
1779
|
23
|
2026-04-23T06:21:35.088298+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925295088_m2.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
6/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.7972075,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"bounds":{"left":0.8125,"top":0.019952115,"width":0.10305851,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.35239363,"top":0.22905028,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.3650266,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Reposit","depth":4,"bounds":{"left":0.37599733,"top":0.22825219,"width":0.051861703,"height":0.015961692},"value":"Reposit","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.4368351,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.44680852,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.4554521,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.46409574,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6/10","depth":4,"bounds":{"left":0.47772607,"top":0.22745411,"width":0.025598405,"height":0.017557861},"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.50332445,"top":0.22665602,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.5119681,"top":0.22665602,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.5206117,"top":0.22665602,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.52925533,"top":0.22665602,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.54587764,"top":0.22665602,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"13","depth":4,"bounds":{"left":0.52327126,"top":0.25778133,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.5349069,"top":0.25778133,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.54454786,"top":0.25618514,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.5518617,"top":0.25618514,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"bounds":{"left":0.69980055,"top":0.15003991,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7117686,"top":0.14844373,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.7190825,"top":0.14844373,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealContactService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsightsCriteriaBuilder.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepositoryInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsServiceRepositories.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PerformanceMonitor.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodOptionDecoratorInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodService.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PeriodServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskTypes","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisk.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRisksServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GroupDealRiskType.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Configuration","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Traits","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"role_description":"text"}]...
|
-2420706914464722987
|
7902282280547189036
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
Reposit
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
6/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface
DealsServiceRepositories.php, class
PerformanceMonitor.php, class
PeriodOptionDecoratorInterface.php, interface
PeriodService.php, final class
PeriodServiceInterface.php, interface
DealRisks
DealRiskTypes
DealRisk.php, class
DealRisksRepository.php, class
DealRisksService.php, class
DealRisksServiceInterface.php, interface
DealRiskType.php
GroupDealRiskType.php
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection
LiveFeed
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration
Console
Commands
Activities
Analytics
Calendars
Crm
Hubspot
IntegrationApp
Traits
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class...
|
72892
|
|
35862
|
731
|
15
|
2026-04-16T10:11:39.837198+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776334299837_m2.jpg...
|
Slack
|
jiminny-x-integration-app (Channel) - Jiminny Inc jiminny-x-integration-app (Channel) - Jiminny Inc - 1 new item - Slack...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Jiminny Inc
Jiminny (Staging)
Add workspaces
Home
Jiminny Inc
Jiminny (Staging)
Add workspaces
Home
Home
DMs
DMs
Activity
Activity
Files
Files
Later
Later
More…
More
Unreads
Threads
Huddles
Drafts & sent
Directories
jiminny-x-integration-app
platform-inner-team
ai-chapter
alerts
backend
confusion-clinic
curiosity_lab
engineering
frontend
general
infra-changes
jiminny-bg
platform-tickets
product_launches
random
releases
sofia-office
support
thank-yous
the_people_of_jiminny
Vasil Vasilev
Galya Dimitrova
Nikolay Ivanov
Aneliya Angelova
Aneliya Angelova
,
Nikolay Yankov
,
Steliyan Georgiev
Stoyan Tanev
Ves
Steliyan Georgiev
Adelina Petrova
,
Ilian Kyuchukov
,
Steliyan Georgiev
Adelina Petrova
Toast
Jira Cloud
Messages
Messages
Add canvas
Add canvas
Files
Files
Pins
Pins
Bookmarks
Bookmarks
Add and Edit Channel Tabs
Canvas
List
Folder
Vlad
Oct 15th, 2024 at 7:47:54 PM
7:47 PM
Hey
@Mo Georgieva
@Mo Georgieva
,
@Vasil Vasilev
@Vasil Vasilev
!
How is your techical evaluation is going? Have you encountered any blockers?
@Ryan
@Ryan
told me today that you are going to build some custom logic on top of our webhooks. Can you please the details? Usually none of the custom logic is needed, mb I can show you the most optimal implementation path
Would be great to sync up this week to review your progress and address any questions. I'd also be happy to jump into a pair-coding session if you need any assistance with our SDK or platform in general
Feel free to book some time that works for you here:
Calendly Link
Calendly Link
.
Looking forward to catching up!
PoC Call
PoC Call
30 mins
Schedule Time
Schedule Time
Added by
a bot
a bot
4 replies
Last reply 2 years ago
View thread
Jump to date
Vasil Vasilev
Oct 16th, 2024 at 4:20:32 PM
4:20 PM
Hey
@Vlad
@Vlad
, sorry for the delay I had to take a bit of time since I managed to get a cold.
(edited)
Vasil Vasilev
Oct 16th, 2024 at 4:26:53 PM
4:26 PM
I will try to catch up these days with the integration, and let you know when I do need some help.
Now about the topics of custom logic - it’s not really a concern, we do store data we pulled from crms locally, sometimes we update it, use it in various terms, and sometimes cross reference it with our own data.
About the SDKs, our primary backend stack is not based on JS, so we will not be using the SDKs initially, at least until we get to the frontend. I will reach out when we are at that point.
And I will send a list of the primary scenarios that we need to execute in a few minutes, I just need to finalise the list.
1 reaction, react with +1 emoji
1
Add reaction…
Pinned by
Vlad
Vlad
Vasil Vasilev
Oct 16th, 2024 at 6:33:04 PM
6:33 PM
Here’s a markdown file with my notes about all scenarios we need to implement, along with a few comments and converns I’ve added while writing it
integration-app-scenarios.md
Toggle file
1
2
#
#
#
#
#
O
r
g
a
n
i
s
a
t
i
o
n
a
l
M
e
t
a
d
a
t
a
3
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
4
...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Jiminny Inc","depth":12,"bounds":{"left":0.00546875,"top":0.05486111,"width":0.0125,"height":0.022222223},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Jiminny (Staging)","depth":12,"bounds":{"left":0.00546875,"top":0.09097222,"width":0.0125,"height":0.022222223},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"Add workspaces","depth":12,"bounds":{"left":0.00546875,"top":0.12708333,"width":0.0125,"height":0.022222223},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Home","depth":14,"bounds":{"left":0.026953125,"top":0.048611112,"width":0.020703126,"height":0.047222223},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Home","depth":16,"bounds":{"left":0.03125,"top":0.08125,"width":0.012109375,"height":0.009027778},"role_description":"text"},{"role":"AXRadioButton","text":"DMs","depth":14,"bounds":{"left":0.026953125,"top":0.09583333,"width":0.020703126,"height":0.047222223},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DMs","depth":16,"bounds":{"left":0.032421876,"top":0.12847222,"width":0.009765625,"height":0.009027778},"role_description":"text"},{"role":"AXRadioButton","text":"Activity","depth":14,"bounds":{"left":0.026953125,"top":0.14305556,"width":0.020703126,"height":0.047222223},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Activity","depth":16,"bounds":{"left":0.0296875,"top":0.17569445,"width":0.015234375,"height":0.009027778},"role_description":"text"},{"role":"AXRadioButton","text":"Files","depth":14,"bounds":{"left":0.026953125,"top":0.19027779,"width":0.020703126,"height":0.047222223},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Files","depth":16,"bounds":{"left":0.0328125,"top":0.22291666,"width":0.008984375,"height":0.009027778},"role_description":"text"},{"role":"AXRadioButton","text":"Later","depth":14,"bounds":{"left":0.026953125,"top":0.2375,"width":0.020703126,"height":0.047222223},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Later","depth":16,"bounds":{"left":0.03203125,"top":0.2701389,"width":0.010546875,"height":0.009027778},"role_description":"text"},{"role":"AXRadioButton","text":"More…","depth":14,"bounds":{"left":0.026953125,"top":0.2847222,"width":0.020703126,"height":0.047222223},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":16,"bounds":{"left":0.03203125,"top":0.31736112,"width":0.010546875,"height":0.009027778},"role_description":"text"},{"role":"AXStaticText","text":"Unreads","depth":20,"bounds":{"left":0.06679688,"top":0.0875,"width":0.022265624,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Threads","depth":20,"bounds":{"left":0.06679688,"top":0.10694444,"width":0.020703126,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Huddles","depth":20,"bounds":{"left":0.06679688,"top":0.12638889,"width":0.021484375,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Drafts & sent","depth":20,"bounds":{"left":0.06679688,"top":0.14583333,"width":0.034375,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Directories","depth":20,"bounds":{"left":0.06679688,"top":0.16527778,"width":0.028515626,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"jiminny-x-integration-app","depth":22,"bounds":{"left":0.07304688,"top":0.24722221,"width":0.0515625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"platform-inner-team","depth":22,"bounds":{"left":0.07304688,"top":0.26666668,"width":0.05234375,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"ai-chapter","depth":22,"bounds":{"left":0.07304688,"top":0.3125,"width":0.026171874,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"alerts","depth":22,"bounds":{"left":0.07304688,"top":0.33194444,"width":0.014453125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"backend","depth":22,"bounds":{"left":0.07304688,"top":0.3513889,"width":0.021484375,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"confusion-clinic","depth":22,"bounds":{"left":0.07304688,"top":0.37083334,"width":0.040625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"curiosity_lab","depth":22,"bounds":{"left":0.07304688,"top":0.39027777,"width":0.032421876,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"engineering","depth":22,"bounds":{"left":0.07304688,"top":0.4097222,"width":0.03125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"frontend","depth":22,"bounds":{"left":0.07304688,"top":0.42916667,"width":0.02265625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"general","depth":22,"bounds":{"left":0.07304688,"top":0.4486111,"width":0.019140625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"infra-changes","depth":22,"bounds":{"left":0.07304688,"top":0.46805555,"width":0.034765624,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"jiminny-bg","depth":22,"bounds":{"left":0.07304688,"top":0.4875,"width":0.02734375,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"platform-tickets","depth":22,"bounds":{"left":0.07304688,"top":0.5069444,"width":0.041015625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"product_launches","depth":22,"bounds":{"left":0.07304688,"top":0.5263889,"width":0.0453125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"random","depth":22,"bounds":{"left":0.07304688,"top":0.54583335,"width":0.019921875,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"releases","depth":22,"bounds":{"left":0.07304688,"top":0.56527776,"width":0.020703126,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"sofia-office","depth":22,"bounds":{"left":0.07304688,"top":0.5847222,"width":0.02890625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"support","depth":22,"bounds":{"left":0.07304688,"top":0.6041667,"width":0.0203125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"thank-yous","depth":22,"bounds":{"left":0.07304688,"top":0.6236111,"width":0.02890625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"the_people_of_jiminny","depth":22,"bounds":{"left":0.07304688,"top":0.64305556,"width":0.053125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Vasil Vasilev","depth":22,"bounds":{"left":0.07304688,"top":0.6888889,"width":0.03125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":22,"bounds":{"left":0.07304688,"top":0.7083333,"width":0.04140625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Ivanov","depth":22,"bounds":{"left":0.07304688,"top":0.7277778,"width":0.037890624,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":22,"bounds":{"left":0.07304688,"top":0.74722224,"width":0.044140626,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":22,"bounds":{"left":0.07304688,"top":0.76666665,"width":0.044140626,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":",","depth":22,"bounds":{"left":0.11679687,"top":0.76666665,"width":0.0078125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Yankov","depth":22,"bounds":{"left":0.11992188,"top":0.76666665,"width":0.016796876,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":",","depth":22,"bounds":{"left":0.13632813,"top":0.78194445,"width":0.000390625,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":22,"bounds":{"left":0.13632813,"top":0.78194445,"width":0.000390625,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Stoyan Tanev","depth":22,"bounds":{"left":0.07304688,"top":0.7861111,"width":0.033984374,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Ves","depth":22,"bounds":{"left":0.07304688,"top":0.8055556,"width":0.009375,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":22,"bounds":{"left":0.07304688,"top":0.825,"width":0.044921875,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Adelina Petrova","depth":22,"bounds":{"left":0.07304688,"top":0.84444445,"width":0.040625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":",","depth":22,"bounds":{"left":0.11328125,"top":0.84444445,"width":0.003125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Ilian Kyuchukov","depth":22,"bounds":{"left":0.11601563,"top":0.84444445,"width":0.009375,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":",","depth":22,"bounds":{"left":0.13632813,"top":0.8597222,"width":0.000390625,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":22,"bounds":{"left":0.13632813,"top":0.8597222,"width":0.000390625,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Adelina Petrova","depth":22,"bounds":{"left":0.07304688,"top":0.86388886,"width":0.040625,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Toast","depth":22,"bounds":{"left":0.07304688,"top":0.9097222,"width":0.014453125,"height":0.0125},"role_description":"text"},{"role":"AXStaticText","text":"Jira Cloud","depth":22,"bounds":{"left":0.07304688,"top":0.9291667,"width":0.02578125,"height":0.0125},"role_description":"text"},{"role":"AXRadioButton","text":"Messages","depth":17,"bounds":{"left":0.14335938,"top":0.07986111,"width":0.036328126,"height":0.02638889},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Messages","depth":19,"bounds":{"left":0.15429688,"top":0.0875,"width":0.022265624,"height":0.011111111},"role_description":"text"},{"role":"AXRadioButton","text":"Add canvas","depth":18,"bounds":{"left":0.18085937,"top":0.07986111,"width":0.040234376,"height":0.02638889},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Add canvas","depth":20,"bounds":{"left":0.19179687,"top":0.0875,"width":0.026171874,"height":0.011111111},"role_description":"text"},{"role":"AXRadioButton","text":"Files","depth":17,"bounds":{"left":0.22226563,"top":0.07986111,"width":0.024609376,"height":0.02638889},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Files","depth":19,"bounds":{"left":0.23320313,"top":0.0875,"width":0.010546875,"height":0.011111111},"role_description":"text"},{"role":"AXRadioButton","text":"Pins","depth":17,"bounds":{"left":0.24804688,"top":0.07986111,"width":0.02421875,"height":0.02638889},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Pins","depth":19,"bounds":{"left":0.2589844,"top":0.0875,"width":0.01015625,"height":0.011111111},"role_description":"text"},{"role":"AXRadioButton","text":"Bookmarks","depth":17,"bounds":{"left":0.2734375,"top":0.07986111,"width":0.03984375,"height":0.02638889},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Bookmarks","depth":19,"bounds":{"left":0.284375,"top":0.0875,"width":0.02578125,"height":0.011111111},"role_description":"text"},{"role":"AXPopUpButton","text":"Add and Edit Channel Tabs","depth":17,"bounds":{"left":0.31445312,"top":0.07986111,"width":0.012890625,"height":0.02638889},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Canvas","depth":17,"bounds":{"left":0.13671875,"top":0.045138888,"width":0.01875,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"List","depth":17,"bounds":{"left":0.13671875,"top":0.045138888,"width":0.009375,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":17,"bounds":{"left":0.13671875,"top":0.045138888,"width":0.01640625,"height":0.00069444446},"role_description":"text"},{"role":"AXButton","text":"Vlad","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.012109375,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":24,"bounds":{"left":0.17382812,"top":0.10069445,"width":0.003515625,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"Oct 15th, 2024 at 7:47:54 PM","depth":24,"bounds":{"left":0.17695312,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"7:47 PM","depth":25,"bounds":{"left":0.17695312,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Hey","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.012109375,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"@Mo Georgieva","depth":24,"bounds":{"left":0.17382812,"top":0.10069445,"width":0.04296875,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"@Mo Georgieva","depth":25,"bounds":{"left":0.17460938,"top":0.10069445,"width":0.04140625,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":",","depth":24,"bounds":{"left":0.21640626,"top":0.10069445,"width":0.003125,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"@Vasil Vasilev","depth":24,"bounds":{"left":0.21914062,"top":0.10069445,"width":0.037890624,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"@Vasil Vasilev","depth":25,"bounds":{"left":0.21992187,"top":0.10069445,"width":0.036328126,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"!","depth":24,"bounds":{"left":0.2566406,"top":0.10069445,"width":0.001953125,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"How is your techical evaluation is going? Have you encountered any blockers?","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.19921875,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"@Ryan","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.01953125,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"@Ryan","depth":25,"bounds":{"left":0.16289063,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"told me today that you are going to build some custom logic on top of our webhooks. Can you please the details? Usually none of the custom logic is needed, mb I can show you the most optimal implementation path","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.3503906,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Would be great to sync up this week to review your progress and address any questions. I'd also be happy to jump into a pair-coding session if you need any assistance with our SDK or platform in general","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.3371094,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Feel free to book some time that works for you here:","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.13671875,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"Calendly Link","depth":24,"bounds":{"left":0.2984375,"top":0.10069445,"width":0.034375,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Calendly Link","depth":25,"bounds":{"left":0.2984375,"top":0.10069445,"width":0.034375,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":".","depth":24,"bounds":{"left":0.33242187,"top":0.10069445,"width":0.001953125,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Looking forward to catching up!","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.08164062,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"PoC Call","depth":27,"bounds":{"left":0.16835937,"top":0.10069445,"width":0.022265624,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"PoC Call","depth":28,"bounds":{"left":0.16835937,"top":0.10069445,"width":0.022265624,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"30 mins","depth":27,"bounds":{"left":0.16835937,"top":0.10069445,"width":0.019921875,"height":0.00069444446},"role_description":"text"},{"role":"AXButton","text":"Schedule Time","depth":26,"bounds":{"left":0.16835937,"top":0.10069445,"width":0.040234376,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Schedule Time","depth":28,"bounds":{"left":0.171875,"top":0.10069445,"width":0.033203125,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Added by","depth":26,"bounds":{"left":0.16835937,"top":0.10069445,"width":0.02109375,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"a bot","depth":26,"bounds":{"left":0.1890625,"top":0.10069445,"width":0.010546875,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"a bot","depth":27,"bounds":{"left":0.1890625,"top":0.10069445,"width":0.010546875,"height":0.00069444446},"role_description":"text"},{"role":"AXButton","text":"4 replies","depth":24,"bounds":{"left":0.18515626,"top":0.10069445,"width":0.019140625,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Last reply 2 years ago","depth":25,"bounds":{"left":0.20703125,"top":0.10069445,"width":0.0484375,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"View thread","depth":25,"bounds":{"left":0.20703125,"top":0.10069445,"width":0.02734375,"height":0.00069444446},"role_description":"text"},{"role":"AXPopUpButton","text":"Jump to date","depth":23,"bounds":{"left":0.29882812,"top":0.10069445,"width":0.0609375,"height":0.00069444446},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Vasil Vasilev","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.032421876,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":24,"bounds":{"left":0.19414063,"top":0.10069445,"width":0.003515625,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"Oct 16th, 2024 at 4:20:32 PM","depth":24,"bounds":{"left":0.19726562,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4:20 PM","depth":25,"bounds":{"left":0.19726562,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Hey","depth":25,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.012109375,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"@Vlad","depth":25,"bounds":{"left":0.17382812,"top":0.10069445,"width":0.018359374,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"@Vlad","depth":26,"bounds":{"left":0.17460938,"top":0.10069445,"width":0.016796876,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":", sorry for the delay I had to take a bit of time since I managed to get a cold.","depth":25,"bounds":{"left":0.19179687,"top":0.10069445,"width":0.19257812,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"bounds":{"left":0.3839844,"top":0.10069445,"width":0.001953125,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"(edited)","depth":25,"bounds":{"left":0.38554686,"top":0.10069445,"width":0.016796876,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"bounds":{"left":0.40195313,"top":0.10069445,"width":0.001953125,"height":0.00069444446},"role_description":"text"},{"role":"AXButton","text":"Vasil Vasilev","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.032421876,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":24,"bounds":{"left":0.19414063,"top":0.10069445,"width":0.003515625,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"Oct 16th, 2024 at 4:26:53 PM","depth":24,"bounds":{"left":0.19726562,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4:26 PM","depth":25,"bounds":{"left":0.19726562,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"I will try to catch up these days with the integration, and let you know when I do need some help.","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.24882813,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Now about the topics of custom logic - it’s not really a concern, we do store data we pulled from crms locally, sometimes we update it, use it in various terms, and sometimes cross reference it with our own data.","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.34414062,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"About the SDKs, our primary backend stack is not based on JS, so we will not be using the SDKs initially, at least until we get to the frontend. I will reach out when we are at that point.","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.33515626,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"And I will send a list of the primary scenarios that we need to execute in a few minutes, I just need to finalise the list.","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.29765624,"height":0.00069444446},"role_description":"text"},{"role":"AXCheckBox","text":"1 reaction, react with +1 emoji","depth":25,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.016796876,"height":0.00069444446},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":26,"bounds":{"left":0.17304687,"top":0.10069445,"width":0.002734375,"height":0.00069444446},"role_description":"text"},{"role":"AXButton","text":"Add reaction…","depth":25,"bounds":{"left":0.18007812,"top":0.10069445,"width":0.013671875,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Pinned by","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.0234375,"height":0.00069444446},"role_description":"text"},{"role":"AXButton","text":"Vlad","depth":24,"bounds":{"left":0.18515626,"top":0.10069445,"width":0.012109375,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Vlad","depth":26,"bounds":{"left":0.18515626,"top":0.10069445,"width":0.010546875,"height":0.00069444446},"role_description":"text"},{"role":"AXButton","text":"Vasil Vasilev","depth":24,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.032421876,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":24,"bounds":{"left":0.19414063,"top":0.10069445,"width":0.003515625,"height":0.00069444446},"role_description":"text"},{"role":"AXLink","text":"Oct 16th, 2024 at 6:33:04 PM","depth":24,"bounds":{"left":0.19726562,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6:33 PM","depth":25,"bounds":{"left":0.19726562,"top":0.10069445,"width":0.01796875,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"Here’s a markdown file with my notes about all scenarios we need to implement, along with a few comments and converns I’ve added while writing it","depth":25,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.34101564,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"integration-app-scenarios.md","depth":25,"bounds":{"left":0.16210938,"top":0.10069445,"width":0.065625,"height":0.00069444446},"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"bounds":{"left":0.22734375,"top":0.10069445,"width":0.0015625,"height":0.00069444446},"role_description":"text"},{"role":"AXButton","text":"Toggle file","depth":25,"bounds":{"left":0.22851562,"top":0.10069445,"width":0.008203125,"height":0.00069444446},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"1","depth":28,"bounds":{"left":0.16992188,"top":0.10069445,"width":0.003125,"height":0.008333334},"role_description":"text"},{"role":"AXStaticText","text":"","depth":28,"role_description":"text"},{"role":"AXStaticText","text":"","depth":28,"bounds":{"left":0.1796875,"top":0.10069445,"width":0.003125,"height":0.008333334},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":28,"bounds":{"left":0.16992188,"top":0.110416666,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"#","depth":28,"bounds":{"left":0.1796875,"top":0.110416666,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"#","depth":28,"bounds":{"left":0.18242188,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"#","depth":28,"bounds":{"left":0.18554688,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"#","depth":28,"bounds":{"left":0.18867187,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"#","depth":28,"bounds":{"left":0.19179687,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"","depth":28,"bounds":{"left":0.19492188,"top":0.110416666,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"O","depth":28,"bounds":{"left":0.19765624,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"r","depth":28,"bounds":{"left":0.20078126,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"g","depth":28,"bounds":{"left":0.20390625,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"a","depth":28,"bounds":{"left":0.20703125,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"n","depth":28,"bounds":{"left":0.21015625,"top":0.110416666,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"i","depth":28,"bounds":{"left":0.21289062,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"s","depth":28,"bounds":{"left":0.21601562,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"a","depth":28,"bounds":{"left":0.21914062,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"t","depth":28,"bounds":{"left":0.22226563,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"i","depth":28,"bounds":{"left":0.22539063,"top":0.110416666,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"o","depth":28,"bounds":{"left":0.22851562,"top":0.110416666,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"n","depth":28,"bounds":{"left":0.23125,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"a","depth":28,"bounds":{"left":0.234375,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"l","depth":28,"bounds":{"left":0.2375,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"","depth":28,"bounds":{"left":0.240625,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"M","depth":28,"bounds":{"left":0.24375,"top":0.110416666,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"e","depth":28,"bounds":{"left":0.24648437,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"t","depth":28,"bounds":{"left":0.24960938,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"a","depth":28,"bounds":{"left":0.25273436,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"d","depth":28,"bounds":{"left":0.25585938,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"a","depth":28,"bounds":{"left":0.2589844,"top":0.110416666,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"t","depth":28,"bounds":{"left":0.26171875,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"a","depth":28,"bounds":{"left":0.26484376,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"","depth":28,"bounds":{"left":0.26796874,"top":0.110416666,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"3","depth":28,"bounds":{"left":0.16992188,"top":0.124305554,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.1796875,"top":0.124305554,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.18242188,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.18554688,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.18867187,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.19179687,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.19492188,"top":0.124305554,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.19765624,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.20078126,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.20390625,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.20703125,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.21015625,"top":0.124305554,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.21289062,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.21601562,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.21914062,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.22226563,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.22539063,"top":0.124305554,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.22851562,"top":0.124305554,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.23125,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.234375,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.2375,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"-","depth":28,"bounds":{"left":0.240625,"top":0.124305554,"width":0.003515625,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"","depth":28,"bounds":{"left":0.24375,"top":0.124305554,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"4","depth":28,"bounds":{"left":0.16992188,"top":0.1375,"width":0.003125,"height":0.011805556},"role_description":"text"},{"role":"AXStaticText","text":"","depth":28,"role_description":"text"},{"role":"AXStaticText","text":"","depth":28,"bounds":{"left":0.1796875,"top":0.1375,"width":0.003125,"height":0.011805556},"role_description":"text"}]...
|
4320653211755699841
|
7901388668180642285
|
visual_change
|
hybrid
|
NULL
|
Jiminny Inc
Jiminny (Staging)
Add workspaces
Home
Jiminny Inc
Jiminny (Staging)
Add workspaces
Home
Home
DMs
DMs
Activity
Activity
Files
Files
Later
Later
More…
More
Unreads
Threads
Huddles
Drafts & sent
Directories
jiminny-x-integration-app
platform-inner-team
ai-chapter
alerts
backend
confusion-clinic
curiosity_lab
engineering
frontend
general
infra-changes
jiminny-bg
platform-tickets
product_launches
random
releases
sofia-office
support
thank-yous
the_people_of_jiminny
Vasil Vasilev
Galya Dimitrova
Nikolay Ivanov
Aneliya Angelova
Aneliya Angelova
,
Nikolay Yankov
,
Steliyan Georgiev
Stoyan Tanev
Ves
Steliyan Georgiev
Adelina Petrova
,
Ilian Kyuchukov
,
Steliyan Georgiev
Adelina Petrova
Toast
Jira Cloud
Messages
Messages
Add canvas
Add canvas
Files
Files
Pins
Pins
Bookmarks
Bookmarks
Add and Edit Channel Tabs
Canvas
List
Folder
Vlad
Oct 15th, 2024 at 7:47:54 PM
7:47 PM
Hey
@Mo Georgieva
@Mo Georgieva
,
@Vasil Vasilev
@Vasil Vasilev
!
How is your techical evaluation is going? Have you encountered any blockers?
@Ryan
@Ryan
told me today that you are going to build some custom logic on top of our webhooks. Can you please the details? Usually none of the custom logic is needed, mb I can show you the most optimal implementation path
Would be great to sync up this week to review your progress and address any questions. I'd also be happy to jump into a pair-coding session if you need any assistance with our SDK or platform in general
Feel free to book some time that works for you here:
Calendly Link
Calendly Link
.
Looking forward to catching up!
PoC Call
PoC Call
30 mins
Schedule Time
Schedule Time
Added by
a bot
a bot
4 replies
Last reply 2 years ago
View thread
Jump to date
Vasil Vasilev
Oct 16th, 2024 at 4:20:32 PM
4:20 PM
Hey
@Vlad
@Vlad
, sorry for the delay I had to take a bit of time since I managed to get a cold.
(edited)
Vasil Vasilev
Oct 16th, 2024 at 4:26:53 PM
4:26 PM
I will try to catch up these days with the integration, and let you know when I do need some help.
Now about the topics of custom logic - it’s not really a concern, we do store data we pulled from crms locally, sometimes we update it, use it in various terms, and sometimes cross reference it with our own data.
About the SDKs, our primary backend stack is not based on JS, so we will not be using the SDKs initially, at least until we get to the frontend. I will reach out when we are at that point.
And I will send a list of the primary scenarios that we need to execute in a few minutes, I just need to finalise the list.
1 reaction, react with +1 emoji
1
Add reaction…
Pinned by
Vlad
Vlad
Vasil Vasilev
Oct 16th, 2024 at 6:33:04 PM
6:33 PM
Here’s a markdown file with my notes about all scenarios we need to implement, along with a few comments and converns I’ve added while writing it
integration-app-scenarios.md
Toggle file
1
2
#
#
#
#
#
O
r
g
a
n
i
s
a
t
i
o
n
a
l
M
e
t
a
d
a
t
a
3
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
4
+SackFileFoitViewHistoryWindowHelpSearch Jiminny IncJiminny ...& jiminny-x-integration-app= Unreads• MessagestP Add canvas4e Files* Pinse) Threads##### Organisational MetadataDMs6d Huddles• Drafts & sent8 DirectoriesAchivityEh External connectionsFiles* Starred@ iminny-x-integrati..platform-inner-team# Channelsi# ai-chaptenMore# alerts# backends contlicion-clinia# curiosity lab# engineering# frontendi# general# infra-changes#: liminny-bg# platform-tickets#: product launchesac random# releases# soha-ofhce# supportac thank-vous# the people of iimi....0 Direct messagesVasil Vasilev% Galya DimitrovaNikolay Ivanov- Aneliva Angeloval3 Aneliva Angelova. ..Stoyan Tanev CVesStelivan Georgiev3 Adelina Petrova, Ili...Adelina Petrova**:AppsToastJira Cloud* 18= BookmarksOctober 23rd, 2024 v0 Hey @Vasil Vasilev! Hope you are feeling better now. How about we make a quick sync in the coming days to check on your progress andaddress any questions vou might have?Please book some time here: [URL_WITH_CREDENTIALS] @Ryan as we're getting closer to get the contract in place we wanted to address one question we had in mind. Do you have asandbox account for MS Dynamics? We wanted to get some insights on it as this will be potentially our next integration. Thank you inadvance!8 3 replies Last reply 1 year agoMaxim Bobritsky 2:09 PMI( was added to jiminny-x-integration-app by Daniil. Also. Pylon joined via inviteNovember 22nd. 2024 vVasil Vasiley 12:26 PMavaeney guys, l just notices a missing icon that is supposed to render on various "Connect pages / widgets"This is the missing url: https://integration.app/images/logo-white-bg.pngAnd this is how it looks in the connection widget for Salesforce.broken-icon.png10 external people are from MembraneMessage 8jiminny-x-integration-appAae SF (iminny@localhostV connect.vue XV Onboard.vuec HS_local fiminny@localhostRODII4 console [STAGING)ionAppOnClicko {s.crul oken.tion = await integrationAppon(this.localProvider.name)nnectiongredBy: false,t1pleconnections: false,' LintegrationApp opennewconnection resouved:, Json.scringlry connectlon)):LonApp openNewconnection resolved:9e0b41a67d0068c2ca0b48e",me":"Zoho CRM",erld":"lece66c8-feb1-4df1-b321-21607daf4623",nantid":"69e0b3faef3e7b6248189289",Test":false.nnected":true,ate": "READY",rors": [],cernaLAppld": "06/1655e/ezdo4zе4e41b0ta",chuptlonkey".""eatedAt":"2026-04-16T10:04:10.420Z",datedAt":"2026-04-16T10:04:10.575Z" ,tryAttempts":0.Deactivated":falseon xx connection.connected === true) 1ction && connection.disconnected === false) {Vereduest = awalt axlos.dosttlV1ЛVSupport Daily • in 1h 49 mA]100% 145Thu 16 Apr 13:11:39CascadeIntegration App ConneWhether it resolves as null/ undefined if the user closes the modalWhat the value is when ©Auth completes successfullyIne key •uestionis wherner connection, dsconnected === talse s actuallvthe right check - or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier).alaealttalatels ein55b7" "name"."ZohoeOafd25ae490e8d05d8ca2" "isTest":false,"142016502c14d""externalA0od":"06/10016T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false}The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-connect S nevercallled atera successul connection.This is the oud. Fix t in ooth connect vue and onboard, vie:• connect.vu+1-3Now fix the same bug in Onboard.vue:Read Onboard vue #L503-517Onboard.vue+1 -1Both files fixed. Summary of the bug:Tihe Sok rerurns connecter: true —there is no disconnected fieldlconnection.disconnected === false was undefined === false →alwavs talse → resnevertred• Same in Onboard.vue: connection.disconnected === true was undefined === true → always talse → always snowed the error snackoarNow after openNewConnection() resolveswith connected: true,the POST /api/v1/integration-app-connect will be called and the SocialAccount will beAsk anything (2+L)+ @ CodeClaude Sonnet 4.6SmTRkt -ERT3N-1qXDRpYmKcbLPMd6v05DLy...LiTo lonKcrorwooLinuouTNovuou4.• provider_refresh_token TeyszanLolgAwmbawmslstnrslyLoLkninicyctwiandolgcuMelstmcpzelotgvhnwrnzorkLTewLUKCNDLKNy04.*_ expires17763361761729613615W Windsurf Teamsuir-oCa 2 space:...
|
NULL
|
|
69502
|
1598
|
2
|
2026-04-22T08:13:15.391414+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776845595391_m1.jpg...
|
iTerm2
|
ec2-user@ip-10-30-159-186:~
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
📝 jobtitle: 2 changes
🔔 Event: creation
📝 jobtitle: 2 changes
🔔 Event: creation
Count: 82
📦 Object Type: deal
🔔 Event: creation
Count: 28
🔔 Event: property_change
Count: 153
Properties:
📝 dealstage: 48 changes
📝 hs_deal_stage_probability: 59 changes
📝 deal_currency_code: 3 changes
📝 amount: 3 changes
📝 closedate: 11 changes
📝 dealtype: 28 changes
📝 dealname: 1 changes
🔔 Event: association_change
Count: 85
📦 Object Type: company
🔔 Event: association_change
Count: 86
🔔 Event: property_change
Count: 11
Properties:
📝 domain: 6 changes
📝 name: 4 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: creation
Count: 4
INFO Looking for metrics: Config 801 (Rise Vision - 852), Date 2026-04-16.
📊 Webhook Metrics for Config 801 (Rise Vision - 852)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: association_change
Count: 57
🔔 Event: property_change
Count: 117
Properties:
📝 closedate: 20 changes
📝 dealstage: 10 changes
📝 hs_deal_stage_probability: 25 changes
📝 hs_manual_forecast_category: 25 changes
📝 amount: 29 changes
📝 dealname: 5 changes
📝 pipeline: 2 changes
📝 hs_next_step: 1 changes
🔔 Event: creation
Count: 16
📦 Object Type: company
🔔 Event: association_change
Count: 149
🔔 Event: creation
Count: 26
🔔 Event: property_change
Count: 73
Properties:
📝 industry: 26 changes
📝 country: 19 changes
📝 name: 26 changes
📝 phone: 1 changes
📝 domain: 1 changes
📦 Object Type: contact
🔔 Event: creation
Count: 95
🔔 Event: association_change
Count: 150
🔔 Event: property_change
Count: 418
Properties:
📝 email: 93 changes
📝 firstname: 90 changes
📝 lastname: 87 changes
📝 associatedcompanyid: 46 changes
📝 phone: 31 changes
📝 jobtitle: 21 changes
📝 hubspot_owner_id: 37 changes
📝 mobilephone: 3 changes
📝 country: 10 changes
INFO Looking for metrics: Config 834 (AnyVan.com - 882), Date 2026-04-16.
📊 Webhook Metrics for Config 834 (AnyVan.com - 882)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 5687
Properties:
📝 name: 4629 changes
📝 domain: 154 changes
📝 hubspot_owner_id: 904 changes
🔔 Event: association_change
Count: 25219
🔔 Event: creation
Count: 4689
📦 Object Type: contact
🔔 Event: property_change
Count: 54620
Properties:
📝 hubspot_owner_id: 2837 changes
📝 email: 4471 changes
📝 phone: 3661 changes
📝 lastname: 16150 changes
📝 firstname: 22622 changes
📝 associatedcompanyid: 4860 changes
📝 mobilephone: 13 changes
📝 jobtitle: 5 changes
📝 country: 1 changes
🔔 Event: creation
Count: 4471
🔔 Event: association_change
Count: 17648
📦 Object Type: deal
🔔 Event: creation
Count: 7588
🔔 Event: property_change
Count: 82720
Properties:
📝 dealname: 4204 changes
📝 amount: 22594 changes
📝 hs_deal_stage_probability: 17646 changes
📝 pipeline: 4356 changes
📝 dealstage: 14626 changes
📝 closedate: 11096 changes
📝 hubspot_owner_id: 6094 changes
📝 selected_date: 1969 changes
📝 deal_currency_code: 135 changes
🔔 Event: association_change
Count: 22773
INFO Looking for metrics: Config 878 (Dingus and Zazzy - 929), Date 2026-04-16.
📊 Webhook Metrics for Config 878 (Dingus and Zazzy - 929)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: property_change
Count: 158
Properties:
📝 amount_in_home_currency: 24 changes
📝 hs_deal_stage_probability: 35 changes
📝 hs_manual_forecast_category: 27 changes
📝 closedate: 23 changes
📝 lost_reason: 9 changes
📝 dealstage: 27 changes
📝 dealname: 9 changes
📝 hubspot_owner_id: 2 changes
📝 source_attribution: 2 changes
🔔 Event: creation
Count: 18
🔔 Event: association_change
Count: 52
📦 Object Type: company
🔔 Event: property_change
Count: 59
Properties:
📝 domain: 14 changes
📝 name: 14 changes
📝 hubspot_owner_id: 17 changes
📝 industry: 5 changes
📝 phone: 4 changes
📝 country: 5 changes
🔔 Event: association_change
Count: 69
🔔 Event: creation
Count: 14
📦 Object Type: contact
🔔 Event: property_change
Count: 192
Properties:
📝 phone: 8 changes
📝 lastname: 22 changes
📝 firstname: 22 changes
📝 jobtitle: 20 changes
📝 hubspot_owner_id: 76 changes
📝 email: 17 changes
📝 associatedcompanyid: 16 changes
📝 mobilephone: 11 changes
🔔 Event: association_change
Count: 53
🔔 Event: creation
Count: 20
INFO Looking for metrics: Config 671 (CosmosID - 691), Date 2026-04-16.
📊 Webhook Metrics for Config 671 (CosmosID - 691)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: creation
Count: 5
🔔 Event: property_change
Count: 15
Properties:
📝 hubspot_owner_id: 6 changes
📝 name: 5 changes
📝 domain: 3 changes
📝 phone: 1 changes
🔔 Event: association_change
Count: 44
📦 Object Type: deal
🔔 Event: property_change
Count: 78
Properties:
📝 hs_deal_stage_probability: 14 changes
📝 createdate: 10 changes
📝 hs_manual_forecast_category: 13 changes
📝 deal_currency_code: 4 changes
📝 amount: 20 changes
📝 closedate: 8 changes
📝 dealstage: 4 changes
📝 dealname: 3 changes
📝 dealtype: 1 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: association_change
Count: 30
🔔 Event: creation
Count: 10
📦 Object Type: contact
🔔 Event: creation
Count: 13
🔔 Event: property_change
Count: 145
Properties:
📝 jobtitle: 77 changes
📝 hubspot_owner_id: 12 changes
📝 firstname: 9 changes
📝 phone: 5 changes
📝 lastname: 8 changes
📝 email: 13 changes
📝 associatedcompanyid: 12 changes
📝 country: 9 changes
🔔 Event: association_change
Count: 34
INFO Looking for metrics: Config 652 (Abode - 673), Date 2026-04-16.
📊 Webhook Metrics for Config 652 (Abode - 673)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 1
Properties:
📝 country: 1 changes
🔔 Event: association_change
Count: 10
📦 Object Type: contact
🔔 Event: creation
Count: 6
🔔 Event: property_change
Count: 40
Properties:
📝 phone: 3 changes
📝 hubspot_owner_id: 8 changes
📝 email: 6 changes
📝 associatedcompanyid: 4 changes
📝 country: 4 changes
📝 lastname: 5 changes
📝 jobtitle: 4 changes
📝 firstname: 5 changes
📝 mobilephone: 1 changes
🔔 Event: association_change
Count: 9
📦 Object Type: deal
🔔 Event: creation
Count: 1
🔔 Event: association_change
Count: 3
🔔 Event: property_change
Count: 13
Properties:
📝 dealstage: 4 changes
📝 hs_deal_stage_probability: 5 changes
📝 dealname: 1 changes
📝 closedate: 1 changes
📝 hs_manual_forecast_category: 1 changes
📝 amount: 1 changes
INFO Looking for metrics: Config 1049 (Classavo - 851), Date 2026-04-16.
📊 Webhook Metrics for Config 1049 (Classavo - 851)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: association_change
Count: 2
📦 Object Type: contact
🔔 Event: association_change
Count: 1
🔔 Event: property_change
Count: 3
Properties:
📝 firstname: 1 changes
📝 lastname: 1 changes
📝 jobtitle: 1 changes
📦 Object Type: deal
🔔 Event: association_change
Count: 3
🔔 Event: property_change
Count: 14
Properties:
📝 dealstage: 4 changes
📝 hs_deal_stage_probability: 5 changes
📝 closedate: 3 changes
📝 deal_currency_code: 1 changes
📝 amount: 1 changes
🔔 Event: creation
Count: 1
INFO Looking for metrics: Config 290 (D1 Training - 308), Date 2026-04-16.
📊 Webhook Metrics for Config 290 (D1 Training - 308)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 5
Properties:
📝 name: 4 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: creation
Count: 1
🔔 Event: association_change
Count: 6
📦 Object Type: deal
🔔 Event: association_change
Count: 40
🔔 Event: creation
Count: 35
🔔 Event: property_change
Count: 405
Properties:
📝 hs_deal_stage_probability: 149 changes
📝 dealtype: 34 changes
📝 dealstage: 114 changes
📝 hubspot_owner_id: 5 changes
📝 closedate: 97 changes
📝 dealname: 3 changes
📝 pipeline: 2 changes
📝 hs_manual_forecast_category: 1 changes
📦 Object Type: contact
🔔 Event: creation
Count: 50
🔔 Event: property_change
Count: 314
Properties:
📝 lastname: 53 changes
📝 email: 46 changes
📝 firstname: 58 changes
📝 mobilephone: 26 changes
📝 phone: 70 changes
📝 hubspot_owner_id: 59 changes
📝 associatedcompanyid: 2 changes
🔔 Event: association_change
Count: 42
INFO Looking for metrics: Config 1019 (SimpleConsign - 1088), Date 2026-04-16.
📊 Webhook Metrics for Config 1019 (SimpleConsign - 1088)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 692
🔔 Event: property_change
Count: 2659
Properties:
📝 email: 334 changes
📝 hubspot_owner_id: 980 changes
📝 associatedcompanyid: 327 changes
📝 lastname: 332 changes
📝 phone: 20 changes
📝 firstname: 333 changes
📝 jobtitle: 321 changes
📝 mobilephone: 7 changes
📝 country: 5 changes
🔔 Event: creation
Count: 335
📦 Object Type: company
🔔 Event: association_change
Count: 698
🔔 Event: property_change
Count: 637
Properties:
📝 domain: 310 changes
📝 name: 312 changes
📝 phone: 5 changes
📝 hubspot_owner_id: 4 changes
📝 country: 3 changes
📝 industry: 3 changes
🔔 Event: creation
Count: 312
📦 Object Type: deal
🔔 Event: association_change
Count: 46
🔔 Event: creation
Count: 9
INFO Looking for metrics: Config 311 (Lemon.io - 329), Date 2026-04-16.
📊 Webhook Metrics for Config 311 (Lemon.io - 329)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 128
Properties:
📝 domain: 26 changes
📝 name: 24 changes
📝 industry: 18 changes
📝 country: 17 changes
📝 hubspot_owner_id: 32 changes
📝 phone: 11 changes
🔔 Event: creation
Count: 26
🔔 Event: association_change
Count: 120
📦 Object Type: deal
🔔 Event: creation
Count: 16
🔔 Event: property_change
Count: 385
Properties:
📝 closedate: 21 changes
📝 dealstage: 49 changes
📝 days_to_close: 19 changes
📝 hs_deal_stage_probability: 58 changes
📝 hs_manual_forecast_category: 34 changes
📝 hs_next_step: 14 changes
📝 level_of_involvement: 15 changes
📝 hubspot_owner_id: 20 changes
📝 freelancers_under_review: 31 changes
📝 pipeline: 8 changes
📝 dealname: 30 changes
📝 deal_currency_code: 8 changes
📝 amount: 13 changes
📝 hs_priority: 8 changes
📝 budget_limitations: 6 changes
📝 need: 7 changes
📝 positive_outcome: 6 changes
📝 competition: 6 changes
📝 negative_consequences: 6 changes
📝 selection_process: 8 changes
📝 project_team_structure: 7 changes
📝 champion: 6 changes
📝 red_flags: 5 changes
🔔 Event: association_change
Count: 63
📦 Object Type: contact
🔔 Event: property_change
Count: 336
Properties:
📝 hubspot_owner_id: 74 changes
📝 email: 56 changes
📝 firstname: 52 changes
📝 lastname: 48 changes
📝 country: 21 changes
📝 jobtitle: 26 changes
📝 associatedcompanyid: 39 changes
📝 phone: 15 changes
📝 mobilephone: 5 changes
🔔 Event: association_change
Count: 103
🔔 Event: creation
Count: 54
INFO Looking for metrics: Config 802 (Street Group - 853), Date 2026-04-16.
📊 Webhook Metrics for Config 802 (Street Group - 853)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: creation
Count: 252
🔔 Event: association_change
Count: 558
🔔 Event: property_change
Count: 1830
Properties:
📝 phone: 170 changes
📝 lastname: 306 changes
📝 firstname: 318 changes
📝 email: 297 changes
📝 country: 287 changes
📝 jobtitle: 208 changes
📝 associatedcompanyid: 180 changes
📝 hubspot_owner_id: 52 changes
📝 mobilephone: 12 changes
📦 Object Type: company
🔔 Event: association_change
Count: 617
🔔 Event: creation
Count: 31
🔔 Event: property_change
Count: 94
Properties:
📝 industry: 6 changes
📝 country: 10 changes
📝 phone: 10 changes
📝 name: 22 changes
📝 domain: 35 changes
📝 hubspot_owner_id: 11 changes
📦 Object Type: deal
🔔 Event: association_change
Count: 121
🔔 Event: property_change
Count: 338
Properties:
📝 hs_deal_stage_probability: 78 changes
📝 deal_currency_code: 22 changes
📝 amount: 36 changes
📝 closedate: 42 changes
📝 dealstage: 91 changes
📝 dealtype: 2 changes
📝 hs_manual_forecast_category: 28 changes
📝 dealname: 8 changes
📝 hs_forecast_probability: 16 changes
📝 invoice_start_date: 8 changes
📝 hubspot_owner_id: 7 changes
🔔 Event: creation
Count: 23
INFO Looking for metrics: Config 1053 (Sensi.AI - 1117), Date 2026-04-16.
📊 Webhook Metrics for Config 1053 (Sensi.AI - 1117)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 156
Properties:
📝 domain: 32 changes
📝 phone: 23 changes
📝 industry: 27 changes
📝 country: 29 changes
📝 name: 39 changes
📝 hubspot_owner_id: 6 changes
🔔 Event: creation
Count: 39
🔔 Event: association_change
Count: 1499
📦 Object Type: deal
🔔 Event: property_change
Count: 491
Properties:
📝 hs_deal_stage_probability: 96 changes
📝 hs_next_step: 46 changes
📝 next_steps_jiminny: 38 changes
📝 hubspot_owner_id: 17 changes
📝 monthly_billable_hours: 10 changes
📝 closedate: 18 changes
📝 amount: 119 changes
📝 deal_currency_code: 115 changes
📝 dealstage: 23 changes
📝 dealname: 4 changes
📝 client_segmentation_jiminny: 1 changes
📝 pain_points__main_objections: 1 changes
📝 product_feedback: 1 changes
📝 red_flags: 1 changes
📝 competitors: 1 changes
🔔 Event: association_change
Count: 98
🔔 Event: creation
Count: 74
📦 Object Type: contact
🔔 Event: association_change
Count: 1577
🔔 Event: property_change
Count: 3768
Properties:
📝 email: 857 changes
📝 mobilephone: 104 changes
📝 lastname: 854 changes
📝 jobtitle: 98 changes
📝 phone: 130 changes
📝 firstname: 870 changes
📝 hubspot_owner_id: 92 changes
📝 associatedcompanyid: 737 changes
📝 country: 26 changes
🔔 Event: creation
Count: 872
INFO Looking for metrics: Config 87 (Repsly - 93), Date 2026-04-16.
📊 Webhook Metrics for Config 87 (Repsly - 93)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 295
🔔 Event: property_change
Count: 1285
Properties:
📝 lastname: 150 changes
📝 email: 128 changes
📝 firstname: 162 changes
📝 associatedcompanyid: 145 changes
📝 country: 130 changes
📝 hubspot_owner_id: 175 changes
📝 mobilephone: 203 changes
📝 phone: 60 changes
📝 jobtitle: 132 changes
🔔 Event: creation
Count: 159
📦 Object Type: deal
🔔 Event: creation
Count: 3
🔔 Event: association_change
Count: 13
🔔 Event: property_change
Count: 23
Properties:
📝 hs_deal_stage_probability: 4 changes
📝 amount: 14 changes
📝 hubspot_owner_id: 1 changes
📝 closedate: 2 changes
📝 hs_next_step: 1 changes
📝 dealstage: 1 changes
📦 Object Type: company
🔔 Event: association_change
Count: 298
🔔 Event: property_change
Count: 23
Properties:
📝 domain: 9 changes
📝 country: 4 changes
📝 name: 6 changes
📝 hubspot_owner_id: 3 changes
📝 phone: 1 changes
🔔 Event: creation
Count: 9
INFO Looking for metrics: Config 518 (Prolific - 544), Date 2026-04-16.
📊 Webhook Metrics for Config 518 (Prolific - 544)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: property_change
Count: 14
Properties:
📝 amount: 3 changes
📝 hs_deal_stage_probability: 4 changes
📝 dealname: 3 changes
📝 closedate: 3 changes
📝 dealstage: 1 changes
🔔 Event: creation
Count: 3
🔔 Event: association_change
Count: 9
📦 Object Type: contact
🔔 Event: creation
Count: 534
🔔 Event: property_change
Count: 7533
Properties:
📝 jobtitle: 73 changes
📝 phone: 2 changes
📝 email: 561 changes
📝 lastname: 81 changes
📝 firstname: 90 changes
📝 associatedcompanyid: 166 changes
📝 hubspot_owner_id: 6151 changes
📝 country: 409 changes
🔔 Event: association_change
Count: 349
📦 Object Type: company
🔔 Event: creation
Count: 30
🔔 Event: association_change
Count: 352
🔔 Event: property_change
Count: 575
Properties:
📝 domain: 30 changes
📝 country: 421 changes
📝 phone: 12 changes
📝 industry: 18 changes
📝 name: 23 changes
📝 hubspot_owner_id: 71 changes
INFO Looking for metrics: Config 761 (Ressio Software - 770), Date 2026-04-16.
📊 Webhook Metrics for Config 761 (Ressio Software - 770)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 1370
Properties:
📝 hubspot_owner_id: 471 changes
📝 domain: 199 changes
📝 name: 202 changes
📝 country: 163 changes
📝 industry: 170 changes
📝 phone: 165 changes
🔔 Event: creation
Count: 207
🔔 Event: association_change
Count: 435
📦 Object Type: contact
🔔 Event: property_change
Count: 1582
Properties:
📝 hubspot_owner_id: 476 changes
📝 phone: 155 changes
📝 mobilephone: 97 changes
📝 lastname: 153 changes
📝 email: 109 changes
📝 associatedcompanyid: 160 changes
📝 firstname: 163 changes
📝 jobtitle: 139 changes
📝 country: 130 changes
🔔 Event: creation
Count: 152
🔔 Event: association_change
Count: 386
📦 Object Type: deal
🔔 Event: property_change
Count: 119
Properties:
📝 closedate: 16 changes
📝 dealstage: 36 changes
📝 hs_deal_stage_probability: 53 changes
📝 hubspot_owner_id: 9 changes
📝 amount: 5 changes
🔔 Event: creation
Count: 17
🔔 Event: association_change
Count: 119
INFO Looking for metrics: Config 537 (Mobiz - 563), Date 2026-04-16.
📊 Webhook Metrics for Config 537 (Mobiz - 563)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: creation
Count: 19
🔔 Event: property_change
Count: 751
Properties:
📝 email: 26 changes
📝 lastname: 18 changes
📝 phone: 15 changes
📝 hubspot_owner_id: 630 changes
📝 firstname: 21 changes
📝 country: 9 changes
📝 jobtitle: 14 changes
📝 associatedcompanyid: 15 changes
📝 mobilephone: 3 changes
🔔 Event: association_change
Count: 35
📦 Object Type: company
🔔 Event: association_change
Count: 36
🔔 Event: property_change
Count: 24
Properties:
📝 hubspot_owner_id: 6 changes
📝 domain: 5 changes
📝 country: 4 changes
📝 industry: 4 changes
📝 name: 4 changes
📝 phone: 1 changes
🔔 Event: creation
Count: 4
📦 Object Type: deal
🔔 Event: property_change
Count: 1
Properties:
📝 hs_deal_stage_probability: 1 changes
🔔 Event: association_change
Count: 3
🔔 Event: creation
Count: 1
INFO Looking for metrics: Config 428 (Welcome to the Jungle UK - 461), Date 2026-04-16.
📊 Webhook Metrics for Config 428 (Welcome to the Jungle UK - 461)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 107
🔔 Event: property_change
Count: 352
Properties:
📝 firstname: 34 changes
📝 email: 35 changes
📝 lastname: 32 changes
📝 associatedcompanyid: 37 changes
📝 country: 29 changes
📝 jobtitle: 29 changes
📝 phone: 43 changes
📝 hubspot_owner_id: 108 changes
📝 mobilephone: 5 changes
🔔 Event: creation
Count: 32
📦 Object Type: deal
🔔 Event: property_change
Count: 225
Properties:
📝 hs_deal_stage_probability: 53 changes
📝 product: 7 changes
📝 closedate: 20 changes
📝 dealstage: 40 changes
📝 hs_manual_forecast_category: 31 changes
📝 amount: 26 changes
📝 deal_currency_code: 17 changes
📝 dealname: 5 changes
📝 segment: 16 changes
📝 hs_next_step: 6 changes
📝 deal_source: 4 changes
🔔 Event: creation
Count: 14
🔔 Event: association_change
Count: 41
📦 Object Type: company
🔔 Event: property_change
Count: 34
Properties:
📝 hubspot_owner_id: 18 changes
📝 domain: 9 changes
📝 name: 7 changes
🔔 Event: creation
Count: 9
🔔 Event: association_change
Count: 130
INFO Looking for metrics: Config 581 (Penfold - 606), Date 2026-04-16.
DOCKER
Close Tab
-zsh
Close Tab
-zsh
Close Tab
✳ Build full day activity summary from Screenpipe (claude)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
APP (-zsh)
Close Tab
ec2-user@ip-10-30-159-186:~ (nc)
Close Tab
⌥⌘1
ec2-user@ip-10-30-159-186:~...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"📝\u0000 jobtitle: 2 changes\n\n 🔔\u0000 Event: creation\n Count: 82\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 28\n\n 🔔\u0000 Event: property_change\n Count: 153\n Properties:\n 📝\u0000 dealstage: 48 changes\n 📝\u0000 hs_deal_stage_probability: 59 changes\n 📝\u0000 deal_currency_code: 3 changes\n 📝\u0000 amount: 3 changes\n 📝\u0000 closedate: 11 changes\n 📝\u0000 dealtype: 28 changes\n 📝\u0000 dealname: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 85\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 86\n\n 🔔\u0000 Event: property_change\n Count: 11\n Properties:\n 📝\u0000 domain: 6 changes\n 📝\u0000 name: 4 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4\n\n\n INFO Looking for metrics: Config 801 (Rise Vision - 852), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 801 (Rise Vision - 852)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 57\n\n 🔔\u0000 Event: property_change\n Count: 117\n Properties:\n 📝\u0000 closedate: 20 changes\n 📝\u0000 dealstage: 10 changes\n 📝\u0000 hs_deal_stage_probability: 25 changes\n 📝\u0000 hs_manual_forecast_category: 25 changes\n 📝\u0000 amount: 29 changes\n 📝\u0000 dealname: 5 changes\n 📝\u0000 pipeline: 2 changes\n 📝\u0000 hs_next_step: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 149\n\n 🔔\u0000 Event: creation\n Count: 26\n\n 🔔\u0000 Event: property_change\n Count: 73\n Properties:\n 📝\u0000 industry: 26 changes\n 📝\u0000 country: 19 changes\n 📝\u0000 name: 26 changes\n 📝\u0000 phone: 1 changes\n 📝\u0000 domain: 1 changes\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 95\n\n 🔔\u0000 Event: association_change\n Count: 150\n\n 🔔\u0000 Event: property_change\n Count: 418\n Properties:\n 📝\u0000 email: 93 changes\n 📝\u0000 firstname: 90 changes\n 📝\u0000 lastname: 87 changes\n 📝\u0000 associatedcompanyid: 46 changes\n 📝\u0000 phone: 31 changes\n 📝\u0000 jobtitle: 21 changes\n 📝\u0000 hubspot_owner_id: 37 changes\n 📝\u0000 mobilephone: 3 changes\n 📝\u0000 country: 10 changes\n\n\n INFO Looking for metrics: Config 834 (AnyVan.com - 882), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 834 (AnyVan.com - 882)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 5687\n Properties:\n 📝\u0000 name: 4629 changes\n 📝\u0000 domain: 154 changes\n 📝\u0000 hubspot_owner_id: 904 changes\n\n 🔔\u0000 Event: association_change\n Count: 25219\n\n 🔔\u0000 Event: creation\n Count: 4689\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 54620\n Properties:\n 📝\u0000 hubspot_owner_id: 2837 changes\n 📝\u0000 email: 4471 changes\n 📝\u0000 phone: 3661 changes\n 📝\u0000 lastname: 16150 changes\n 📝\u0000 firstname: 22622 changes\n 📝\u0000 associatedcompanyid: 4860 changes\n 📝\u0000 mobilephone: 13 changes\n 📝\u0000 jobtitle: 5 changes\n 📝\u0000 country: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4471\n\n 🔔\u0000 Event: association_change\n Count: 17648\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 7588\n\n 🔔\u0000 Event: property_change\n Count: 82720\n Properties:\n 📝\u0000 dealname: 4204 changes\n 📝\u0000 amount: 22594 changes\n 📝\u0000 hs_deal_stage_probability: 17646 changes\n 📝\u0000 pipeline: 4356 changes\n 📝\u0000 dealstage: 14626 changes\n 📝\u0000 closedate: 11096 changes\n 📝\u0000 hubspot_owner_id: 6094 changes\n 📝\u0000 selected_date: 1969 changes\n 📝\u0000 deal_currency_code: 135 changes\n\n 🔔\u0000 Event: association_change\n Count: 22773\n\n\n INFO Looking for metrics: Config 878 (Dingus and Zazzy - 929), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 878 (Dingus and Zazzy - 929)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 158\n Properties:\n 📝\u0000 amount_in_home_currency: 24 changes\n 📝\u0000 hs_deal_stage_probability: 35 changes\n 📝\u0000 hs_manual_forecast_category: 27 changes\n 📝\u0000 closedate: 23 changes\n 📝\u0000 lost_reason: 9 changes\n 📝\u0000 dealstage: 27 changes\n 📝\u0000 dealname: 9 changes\n 📝\u0000 hubspot_owner_id: 2 changes\n 📝\u0000 source_attribution: 2 changes\n\n 🔔\u0000 Event: creation\n Count: 18\n\n 🔔\u0000 Event: association_change\n Count: 52\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 59\n Properties:\n 📝\u0000 domain: 14 changes\n 📝\u0000 name: 14 changes\n 📝\u0000 hubspot_owner_id: 17 changes\n 📝\u0000 industry: 5 changes\n 📝\u0000 phone: 4 changes\n 📝\u0000 country: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 69\n\n 🔔\u0000 Event: creation\n Count: 14\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 192\n Properties:\n 📝\u0000 phone: 8 changes\n 📝\u0000 lastname: 22 changes\n 📝\u0000 firstname: 22 changes\n 📝\u0000 jobtitle: 20 changes\n 📝\u0000 hubspot_owner_id: 76 changes\n 📝\u0000 email: 17 changes\n 📝\u0000 associatedcompanyid: 16 changes\n 📝\u0000 mobilephone: 11 changes\n\n 🔔\u0000 Event: association_change\n Count: 53\n\n 🔔\u0000 Event: creation\n Count: 20\n\n\n INFO Looking for metrics: Config 671 (CosmosID - 691), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 671 (CosmosID - 691)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 5\n\n 🔔\u0000 Event: property_change\n Count: 15\n Properties:\n 📝\u0000 hubspot_owner_id: 6 changes\n 📝\u0000 name: 5 changes\n 📝\u0000 domain: 3 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 44\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 78\n Properties:\n 📝\u0000 hs_deal_stage_probability: 14 changes\n 📝\u0000 createdate: 10 changes\n 📝\u0000 hs_manual_forecast_category: 13 changes\n 📝\u0000 deal_currency_code: 4 changes\n 📝\u0000 amount: 20 changes\n 📝\u0000 closedate: 8 changes\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 dealtype: 1 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 30\n\n 🔔\u0000 Event: creation\n Count: 10\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 13\n\n 🔔\u0000 Event: property_change\n Count: 145\n Properties:\n 📝\u0000 jobtitle: 77 changes\n 📝\u0000 hubspot_owner_id: 12 changes\n 📝\u0000 firstname: 9 changes\n 📝\u0000 phone: 5 changes\n 📝\u0000 lastname: 8 changes\n 📝\u0000 email: 13 changes\n 📝\u0000 associatedcompanyid: 12 changes\n 📝\u0000 country: 9 changes\n\n 🔔\u0000 Event: association_change\n Count: 34\n\n\n INFO Looking for metrics: Config 652 (Abode - 673), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 652 (Abode - 673)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 1\n Properties:\n 📝\u0000 country: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 10\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 6\n\n 🔔\u0000 Event: property_change\n Count: 40\n Properties:\n 📝\u0000 phone: 3 changes\n 📝\u0000 hubspot_owner_id: 8 changes\n 📝\u0000 email: 6 changes\n 📝\u0000 associatedcompanyid: 4 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 lastname: 5 changes\n 📝\u0000 jobtitle: 4 changes\n 📝\u0000 firstname: 5 changes\n 📝\u0000 mobilephone: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 9\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 1\n\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: property_change\n Count: 13\n Properties:\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 hs_deal_stage_probability: 5 changes\n 📝\u0000 dealname: 1 changes\n 📝\u0000 closedate: 1 changes\n 📝\u0000 hs_manual_forecast_category: 1 changes\n 📝\u0000 amount: 1 changes\n\n\n INFO Looking for metrics: Config 1049 (Classavo - 851), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1049 (Classavo - 851)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 2\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 1\n\n 🔔\u0000 Event: property_change\n Count: 3\n Properties:\n 📝\u0000 firstname: 1 changes\n 📝\u0000 lastname: 1 changes\n 📝\u0000 jobtitle: 1 changes\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: property_change\n Count: 14\n Properties:\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 hs_deal_stage_probability: 5 changes\n 📝\u0000 closedate: 3 changes\n 📝\u0000 deal_currency_code: 1 changes\n 📝\u0000 amount: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 1\n\n\n INFO Looking for metrics: Config 290 (D1 Training - 308), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 290 (D1 Training - 308)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 5\n Properties:\n 📝\u0000 name: 4 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 1\n\n 🔔\u0000 Event: association_change\n Count: 6\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 40\n\n 🔔\u0000 Event: creation\n Count: 35\n\n 🔔\u0000 Event: property_change\n Count: 405\n Properties:\n 📝\u0000 hs_deal_stage_probability: 149 changes\n 📝\u0000 dealtype: 34 changes\n 📝\u0000 dealstage: 114 changes\n 📝\u0000 hubspot_owner_id: 5 changes\n 📝\u0000 closedate: 97 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 pipeline: 2 changes\n 📝\u0000 hs_manual_forecast_category: 1 changes\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 50\n\n 🔔\u0000 Event: property_change\n Count: 314\n Properties:\n 📝\u0000 lastname: 53 changes\n 📝\u0000 email: 46 changes\n 📝\u0000 firstname: 58 changes\n 📝\u0000 mobilephone: 26 changes\n 📝\u0000 phone: 70 changes\n 📝\u0000 hubspot_owner_id: 59 changes\n 📝\u0000 associatedcompanyid: 2 changes\n\n 🔔\u0000 Event: association_change\n Count: 42\n\n\n INFO Looking for metrics: Config 1019 (SimpleConsign - 1088), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1019 (SimpleConsign - 1088)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 692\n\n 🔔\u0000 Event: property_change\n Count: 2659\n Properties:\n 📝\u0000 email: 334 changes\n 📝\u0000 hubspot_owner_id: 980 changes\n 📝\u0000 associatedcompanyid: 327 changes\n 📝\u0000 lastname: 332 changes\n 📝\u0000 phone: 20 changes\n 📝\u0000 firstname: 333 changes\n 📝\u0000 jobtitle: 321 changes\n 📝\u0000 mobilephone: 7 changes\n 📝\u0000 country: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 335\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 698\n\n 🔔\u0000 Event: property_change\n Count: 637\n Properties:\n 📝\u0000 domain: 310 changes\n 📝\u0000 name: 312 changes\n 📝\u0000 phone: 5 changes\n 📝\u0000 hubspot_owner_id: 4 changes\n 📝\u0000 country: 3 changes\n 📝\u0000 industry: 3 changes\n\n 🔔\u0000 Event: creation\n Count: 312\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 46\n\n 🔔\u0000 Event: creation\n Count: 9\n\n\n INFO Looking for metrics: Config 311 (Lemon.io - 329), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 311 (Lemon.io - 329)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 128\n Properties:\n 📝\u0000 domain: 26 changes\n 📝\u0000 name: 24 changes\n 📝\u0000 industry: 18 changes\n 📝\u0000 country: 17 changes\n 📝\u0000 hubspot_owner_id: 32 changes\n 📝\u0000 phone: 11 changes\n\n 🔔\u0000 Event: creation\n Count: 26\n\n 🔔\u0000 Event: association_change\n Count: 120\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 16\n\n 🔔\u0000 Event: property_change\n Count: 385\n Properties:\n 📝\u0000 closedate: 21 changes\n 📝\u0000 dealstage: 49 changes\n 📝\u0000 days_to_close: 19 changes\n 📝\u0000 hs_deal_stage_probability: 58 changes\n 📝\u0000 hs_manual_forecast_category: 34 changes\n 📝\u0000 hs_next_step: 14 changes\n 📝\u0000 level_of_involvement: 15 changes\n 📝\u0000 hubspot_owner_id: 20 changes\n 📝\u0000 freelancers_under_review: 31 changes\n 📝\u0000 pipeline: 8 changes\n 📝\u0000 dealname: 30 changes\n 📝\u0000 deal_currency_code: 8 changes\n 📝\u0000 amount: 13 changes\n 📝\u0000 hs_priority: 8 changes\n 📝\u0000 budget_limitations: 6 changes\n 📝\u0000 need: 7 changes\n 📝\u0000 positive_outcome: 6 changes\n 📝\u0000 competition: 6 changes\n 📝\u0000 negative_consequences: 6 changes\n 📝\u0000 selection_process: 8 changes\n 📝\u0000 project_team_structure: 7 changes\n 📝\u0000 champion: 6 changes\n 📝\u0000 red_flags: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 63\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 336\n Properties:\n 📝\u0000 hubspot_owner_id: 74 changes\n 📝\u0000 email: 56 changes\n 📝\u0000 firstname: 52 changes\n 📝\u0000 lastname: 48 changes\n 📝\u0000 country: 21 changes\n 📝\u0000 jobtitle: 26 changes\n 📝\u0000 associatedcompanyid: 39 changes\n 📝\u0000 phone: 15 changes\n 📝\u0000 mobilephone: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 103\n\n 🔔\u0000 Event: creation\n Count: 54\n\n\n INFO Looking for metrics: Config 802 (Street Group - 853), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 802 (Street Group - 853)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 252\n\n 🔔\u0000 Event: association_change\n Count: 558\n\n 🔔\u0000 Event: property_change\n Count: 1830\n Properties:\n 📝\u0000 phone: 170 changes\n 📝\u0000 lastname: 306 changes\n 📝\u0000 firstname: 318 changes\n 📝\u0000 email: 297 changes\n 📝\u0000 country: 287 changes\n 📝\u0000 jobtitle: 208 changes\n 📝\u0000 associatedcompanyid: 180 changes\n 📝\u0000 hubspot_owner_id: 52 changes\n 📝\u0000 mobilephone: 12 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 617\n\n 🔔\u0000 Event: creation\n Count: 31\n\n 🔔\u0000 Event: property_change\n Count: 94\n Properties:\n 📝\u0000 industry: 6 changes\n 📝\u0000 country: 10 changes\n 📝\u0000 phone: 10 changes\n 📝\u0000 name: 22 changes\n 📝\u0000 domain: 35 changes\n 📝\u0000 hubspot_owner_id: 11 changes\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 121\n\n 🔔\u0000 Event: property_change\n Count: 338\n Properties:\n 📝\u0000 hs_deal_stage_probability: 78 changes\n 📝\u0000 deal_currency_code: 22 changes\n 📝\u0000 amount: 36 changes\n 📝\u0000 closedate: 42 changes\n 📝\u0000 dealstage: 91 changes\n 📝\u0000 dealtype: 2 changes\n 📝\u0000 hs_manual_forecast_category: 28 changes\n 📝\u0000 dealname: 8 changes\n 📝\u0000 hs_forecast_probability: 16 changes\n 📝\u0000 invoice_start_date: 8 changes\n 📝\u0000 hubspot_owner_id: 7 changes\n\n 🔔\u0000 Event: creation\n Count: 23\n\n\n INFO Looking for metrics: Config 1053 (Sensi.AI - 1117), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1053 (Sensi.AI - 1117)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 156\n Properties:\n 📝\u0000 domain: 32 changes\n 📝\u0000 phone: 23 changes\n 📝\u0000 industry: 27 changes\n 📝\u0000 country: 29 changes\n 📝\u0000 name: 39 changes\n 📝\u0000 hubspot_owner_id: 6 changes\n\n 🔔\u0000 Event: creation\n Count: 39\n\n 🔔\u0000 Event: association_change\n Count: 1499\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 491\n Properties:\n 📝\u0000 hs_deal_stage_probability: 96 changes\n 📝\u0000 hs_next_step: 46 changes\n 📝\u0000 next_steps_jiminny: 38 changes\n 📝\u0000 hubspot_owner_id: 17 changes\n 📝\u0000 monthly_billable_hours: 10 changes\n 📝\u0000 closedate: 18 changes\n 📝\u0000 amount: 119 changes\n 📝\u0000 deal_currency_code: 115 changes\n 📝\u0000 dealstage: 23 changes\n 📝\u0000 dealname: 4 changes\n 📝\u0000 client_segmentation_jiminny: 1 changes\n 📝\u0000 pain_points__main_objections: 1 changes\n 📝\u0000 product_feedback: 1 changes\n 📝\u0000 red_flags: 1 changes\n 📝\u0000 competitors: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 98\n\n 🔔\u0000 Event: creation\n Count: 74\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 1577\n\n 🔔\u0000 Event: property_change\n Count: 3768\n Properties:\n 📝\u0000 email: 857 changes\n 📝\u0000 mobilephone: 104 changes\n 📝\u0000 lastname: 854 changes\n 📝\u0000 jobtitle: 98 changes\n 📝\u0000 phone: 130 changes\n 📝\u0000 firstname: 870 changes\n 📝\u0000 hubspot_owner_id: 92 changes\n 📝\u0000 associatedcompanyid: 737 changes\n 📝\u0000 country: 26 changes\n\n 🔔\u0000 Event: creation\n Count: 872\n\n\n INFO Looking for metrics: Config 87 (Repsly - 93), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 87 (Repsly - 93)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 295\n\n 🔔\u0000 Event: property_change\n Count: 1285\n Properties:\n 📝\u0000 lastname: 150 changes\n 📝\u0000 email: 128 changes\n 📝\u0000 firstname: 162 changes\n 📝\u0000 associatedcompanyid: 145 changes\n 📝\u0000 country: 130 changes\n 📝\u0000 hubspot_owner_id: 175 changes\n 📝\u0000 mobilephone: 203 changes\n 📝\u0000 phone: 60 changes\n 📝\u0000 jobtitle: 132 changes\n\n 🔔\u0000 Event: creation\n Count: 159\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 3\n\n 🔔\u0000 Event: association_change\n Count: 13\n\n 🔔\u0000 Event: property_change\n Count: 23\n Properties:\n 📝\u0000 hs_deal_stage_probability: 4 changes\n 📝\u0000 amount: 14 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n 📝\u0000 closedate: 2 changes\n 📝\u0000 hs_next_step: 1 changes\n 📝\u0000 dealstage: 1 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 298\n\n 🔔\u0000 Event: property_change\n Count: 23\n Properties:\n 📝\u0000 domain: 9 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 name: 6 changes\n 📝\u0000 hubspot_owner_id: 3 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 9\n\n\n INFO Looking for metrics: Config 518 (Prolific - 544), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 518 (Prolific - 544)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 14\n Properties:\n 📝\u0000 amount: 3 changes\n 📝\u0000 hs_deal_stage_probability: 4 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 closedate: 3 changes\n 📝\u0000 dealstage: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 3\n\n 🔔\u0000 Event: association_change\n Count: 9\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 534\n\n 🔔\u0000 Event: property_change\n Count: 7533\n Properties:\n 📝\u0000 jobtitle: 73 changes\n 📝\u0000 phone: 2 changes\n 📝\u0000 email: 561 changes\n 📝\u0000 lastname: 81 changes\n 📝\u0000 firstname: 90 changes\n 📝\u0000 associatedcompanyid: 166 changes\n 📝\u0000 hubspot_owner_id: 6151 changes\n 📝\u0000 country: 409 changes\n\n 🔔\u0000 Event: association_change\n Count: 349\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 30\n\n 🔔\u0000 Event: association_change\n Count: 352\n\n 🔔\u0000 Event: property_change\n Count: 575\n Properties:\n 📝\u0000 domain: 30 changes\n 📝\u0000 country: 421 changes\n 📝\u0000 phone: 12 changes\n 📝\u0000 industry: 18 changes\n 📝\u0000 name: 23 changes\n 📝\u0000 hubspot_owner_id: 71 changes\n\n\n INFO Looking for metrics: Config 761 (Ressio Software - 770), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 761 (Ressio Software - 770)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 1370\n Properties:\n 📝\u0000 hubspot_owner_id: 471 changes\n 📝\u0000 domain: 199 changes\n 📝\u0000 name: 202 changes\n 📝\u0000 country: 163 changes\n 📝\u0000 industry: 170 changes\n 📝\u0000 phone: 165 changes\n\n 🔔\u0000 Event: creation\n Count: 207\n\n 🔔\u0000 Event: association_change\n Count: 435\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 1582\n Properties:\n 📝\u0000 hubspot_owner_id: 476 changes\n 📝\u0000 phone: 155 changes\n 📝\u0000 mobilephone: 97 changes\n 📝\u0000 lastname: 153 changes\n 📝\u0000 email: 109 changes\n 📝\u0000 associatedcompanyid: 160 changes\n 📝\u0000 firstname: 163 changes\n 📝\u0000 jobtitle: 139 changes\n 📝\u0000 country: 130 changes\n\n 🔔\u0000 Event: creation\n Count: 152\n\n 🔔\u0000 Event: association_change\n Count: 386\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 119\n Properties:\n 📝\u0000 closedate: 16 changes\n 📝\u0000 dealstage: 36 changes\n 📝\u0000 hs_deal_stage_probability: 53 changes\n 📝\u0000 hubspot_owner_id: 9 changes\n 📝\u0000 amount: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 17\n\n 🔔\u0000 Event: association_change\n Count: 119\n\n\n INFO Looking for metrics: Config 537 (Mobiz - 563), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 537 (Mobiz - 563)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 19\n\n 🔔\u0000 Event: property_change\n Count: 751\n Properties:\n 📝\u0000 email: 26 changes\n 📝\u0000 lastname: 18 changes\n 📝\u0000 phone: 15 changes\n 📝\u0000 hubspot_owner_id: 630 changes\n 📝\u0000 firstname: 21 changes\n 📝\u0000 country: 9 changes\n 📝\u0000 jobtitle: 14 changes\n 📝\u0000 associatedcompanyid: 15 changes\n 📝\u0000 mobilephone: 3 changes\n\n 🔔\u0000 Event: association_change\n Count: 35\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 36\n\n 🔔\u0000 Event: property_change\n Count: 24\n Properties:\n 📝\u0000 hubspot_owner_id: 6 changes\n 📝\u0000 domain: 5 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 industry: 4 changes\n 📝\u0000 name: 4 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 1\n Properties:\n 📝\u0000 hs_deal_stage_probability: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: creation\n Count: 1\n\n\n INFO Looking for metrics: Config 428 (Welcome to the Jungle UK - 461), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 428 (Welcome to the Jungle UK - 461)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 107\n\n 🔔\u0000 Event: property_change\n Count: 352\n Properties:\n 📝\u0000 firstname: 34 changes\n 📝\u0000 email: 35 changes\n 📝\u0000 lastname: 32 changes\n 📝\u0000 associatedcompanyid: 37 changes\n 📝\u0000 country: 29 changes\n 📝\u0000 jobtitle: 29 changes\n 📝\u0000 phone: 43 changes\n 📝\u0000 hubspot_owner_id: 108 changes\n 📝\u0000 mobilephone: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 32\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 225\n Properties:\n 📝\u0000 hs_deal_stage_probability: 53 changes\n 📝\u0000 product: 7 changes\n 📝\u0000 closedate: 20 changes\n 📝\u0000 dealstage: 40 changes\n 📝\u0000 hs_manual_forecast_category: 31 changes\n 📝\u0000 amount: 26 changes\n 📝\u0000 deal_currency_code: 17 changes\n 📝\u0000 dealname: 5 changes\n 📝\u0000 segment: 16 changes\n 📝\u0000 hs_next_step: 6 changes\n 📝\u0000 deal_source: 4 changes\n\n 🔔\u0000 Event: creation\n Count: 14\n\n 🔔\u0000 Event: association_change\n Count: 41\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 34\n Properties:\n 📝\u0000 hubspot_owner_id: 18 changes\n 📝\u0000 domain: 9 changes\n 📝\u0000 name: 7 changes\n\n 🔔\u0000 Event: creation\n Count: 9\n\n 🔔\u0000 Event: association_change\n Count: 130\n\n\n INFO Looking for metrics: Config 581 (Penfold - 606), Date 2026-04-16.","depth":4,"value":"📝\u0000 jobtitle: 2 changes\n\n 🔔\u0000 Event: creation\n Count: 82\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 28\n\n 🔔\u0000 Event: property_change\n Count: 153\n Properties:\n 📝\u0000 dealstage: 48 changes\n 📝\u0000 hs_deal_stage_probability: 59 changes\n 📝\u0000 deal_currency_code: 3 changes\n 📝\u0000 amount: 3 changes\n 📝\u0000 closedate: 11 changes\n 📝\u0000 dealtype: 28 changes\n 📝\u0000 dealname: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 85\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 86\n\n 🔔\u0000 Event: property_change\n Count: 11\n Properties:\n 📝\u0000 domain: 6 changes\n 📝\u0000 name: 4 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4\n\n\n INFO Looking for metrics: Config 801 (Rise Vision - 852), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 801 (Rise Vision - 852)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 57\n\n 🔔\u0000 Event: property_change\n Count: 117\n Properties:\n 📝\u0000 closedate: 20 changes\n 📝\u0000 dealstage: 10 changes\n 📝\u0000 hs_deal_stage_probability: 25 changes\n 📝\u0000 hs_manual_forecast_category: 25 changes\n 📝\u0000 amount: 29 changes\n 📝\u0000 dealname: 5 changes\n 📝\u0000 pipeline: 2 changes\n 📝\u0000 hs_next_step: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 149\n\n 🔔\u0000 Event: creation\n Count: 26\n\n 🔔\u0000 Event: property_change\n Count: 73\n Properties:\n 📝\u0000 industry: 26 changes\n 📝\u0000 country: 19 changes\n 📝\u0000 name: 26 changes\n 📝\u0000 phone: 1 changes\n 📝\u0000 domain: 1 changes\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 95\n\n 🔔\u0000 Event: association_change\n Count: 150\n\n 🔔\u0000 Event: property_change\n Count: 418\n Properties:\n 📝\u0000 email: 93 changes\n 📝\u0000 firstname: 90 changes\n 📝\u0000 lastname: 87 changes\n 📝\u0000 associatedcompanyid: 46 changes\n 📝\u0000 phone: 31 changes\n 📝\u0000 jobtitle: 21 changes\n 📝\u0000 hubspot_owner_id: 37 changes\n 📝\u0000 mobilephone: 3 changes\n 📝\u0000 country: 10 changes\n\n\n INFO Looking for metrics: Config 834 (AnyVan.com - 882), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 834 (AnyVan.com - 882)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 5687\n Properties:\n 📝\u0000 name: 4629 changes\n 📝\u0000 domain: 154 changes\n 📝\u0000 hubspot_owner_id: 904 changes\n\n 🔔\u0000 Event: association_change\n Count: 25219\n\n 🔔\u0000 Event: creation\n Count: 4689\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 54620\n Properties:\n 📝\u0000 hubspot_owner_id: 2837 changes\n 📝\u0000 email: 4471 changes\n 📝\u0000 phone: 3661 changes\n 📝\u0000 lastname: 16150 changes\n 📝\u0000 firstname: 22622 changes\n 📝\u0000 associatedcompanyid: 4860 changes\n 📝\u0000 mobilephone: 13 changes\n 📝\u0000 jobtitle: 5 changes\n 📝\u0000 country: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4471\n\n 🔔\u0000 Event: association_change\n Count: 17648\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 7588\n\n 🔔\u0000 Event: property_change\n Count: 82720\n Properties:\n 📝\u0000 dealname: 4204 changes\n 📝\u0000 amount: 22594 changes\n 📝\u0000 hs_deal_stage_probability: 17646 changes\n 📝\u0000 pipeline: 4356 changes\n 📝\u0000 dealstage: 14626 changes\n 📝\u0000 closedate: 11096 changes\n 📝\u0000 hubspot_owner_id: 6094 changes\n 📝\u0000 selected_date: 1969 changes\n 📝\u0000 deal_currency_code: 135 changes\n\n 🔔\u0000 Event: association_change\n Count: 22773\n\n\n INFO Looking for metrics: Config 878 (Dingus and Zazzy - 929), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 878 (Dingus and Zazzy - 929)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 158\n Properties:\n 📝\u0000 amount_in_home_currency: 24 changes\n 📝\u0000 hs_deal_stage_probability: 35 changes\n 📝\u0000 hs_manual_forecast_category: 27 changes\n 📝\u0000 closedate: 23 changes\n 📝\u0000 lost_reason: 9 changes\n 📝\u0000 dealstage: 27 changes\n 📝\u0000 dealname: 9 changes\n 📝\u0000 hubspot_owner_id: 2 changes\n 📝\u0000 source_attribution: 2 changes\n\n 🔔\u0000 Event: creation\n Count: 18\n\n 🔔\u0000 Event: association_change\n Count: 52\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 59\n Properties:\n 📝\u0000 domain: 14 changes\n 📝\u0000 name: 14 changes\n 📝\u0000 hubspot_owner_id: 17 changes\n 📝\u0000 industry: 5 changes\n 📝\u0000 phone: 4 changes\n 📝\u0000 country: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 69\n\n 🔔\u0000 Event: creation\n Count: 14\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 192\n Properties:\n 📝\u0000 phone: 8 changes\n 📝\u0000 lastname: 22 changes\n 📝\u0000 firstname: 22 changes\n 📝\u0000 jobtitle: 20 changes\n 📝\u0000 hubspot_owner_id: 76 changes\n 📝\u0000 email: 17 changes\n 📝\u0000 associatedcompanyid: 16 changes\n 📝\u0000 mobilephone: 11 changes\n\n 🔔\u0000 Event: association_change\n Count: 53\n\n 🔔\u0000 Event: creation\n Count: 20\n\n\n INFO Looking for metrics: Config 671 (CosmosID - 691), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 671 (CosmosID - 691)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 5\n\n 🔔\u0000 Event: property_change\n Count: 15\n Properties:\n 📝\u0000 hubspot_owner_id: 6 changes\n 📝\u0000 name: 5 changes\n 📝\u0000 domain: 3 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 44\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 78\n Properties:\n 📝\u0000 hs_deal_stage_probability: 14 changes\n 📝\u0000 createdate: 10 changes\n 📝\u0000 hs_manual_forecast_category: 13 changes\n 📝\u0000 deal_currency_code: 4 changes\n 📝\u0000 amount: 20 changes\n 📝\u0000 closedate: 8 changes\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 dealtype: 1 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 30\n\n 🔔\u0000 Event: creation\n Count: 10\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 13\n\n 🔔\u0000 Event: property_change\n Count: 145\n Properties:\n 📝\u0000 jobtitle: 77 changes\n 📝\u0000 hubspot_owner_id: 12 changes\n 📝\u0000 firstname: 9 changes\n 📝\u0000 phone: 5 changes\n 📝\u0000 lastname: 8 changes\n 📝\u0000 email: 13 changes\n 📝\u0000 associatedcompanyid: 12 changes\n 📝\u0000 country: 9 changes\n\n 🔔\u0000 Event: association_change\n Count: 34\n\n\n INFO Looking for metrics: Config 652 (Abode - 673), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 652 (Abode - 673)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 1\n Properties:\n 📝\u0000 country: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 10\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 6\n\n 🔔\u0000 Event: property_change\n Count: 40\n Properties:\n 📝\u0000 phone: 3 changes\n 📝\u0000 hubspot_owner_id: 8 changes\n 📝\u0000 email: 6 changes\n 📝\u0000 associatedcompanyid: 4 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 lastname: 5 changes\n 📝\u0000 jobtitle: 4 changes\n 📝\u0000 firstname: 5 changes\n 📝\u0000 mobilephone: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 9\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 1\n\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: property_change\n Count: 13\n Properties:\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 hs_deal_stage_probability: 5 changes\n 📝\u0000 dealname: 1 changes\n 📝\u0000 closedate: 1 changes\n 📝\u0000 hs_manual_forecast_category: 1 changes\n 📝\u0000 amount: 1 changes\n\n\n INFO Looking for metrics: Config 1049 (Classavo - 851), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1049 (Classavo - 851)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 2\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 1\n\n 🔔\u0000 Event: property_change\n Count: 3\n Properties:\n 📝\u0000 firstname: 1 changes\n 📝\u0000 lastname: 1 changes\n 📝\u0000 jobtitle: 1 changes\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: property_change\n Count: 14\n Properties:\n 📝\u0000 dealstage: 4 changes\n 📝\u0000 hs_deal_stage_probability: 5 changes\n 📝\u0000 closedate: 3 changes\n 📝\u0000 deal_currency_code: 1 changes\n 📝\u0000 amount: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 1\n\n\n INFO Looking for metrics: Config 290 (D1 Training - 308), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 290 (D1 Training - 308)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 5\n Properties:\n 📝\u0000 name: 4 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 1\n\n 🔔\u0000 Event: association_change\n Count: 6\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 40\n\n 🔔\u0000 Event: creation\n Count: 35\n\n 🔔\u0000 Event: property_change\n Count: 405\n Properties:\n 📝\u0000 hs_deal_stage_probability: 149 changes\n 📝\u0000 dealtype: 34 changes\n 📝\u0000 dealstage: 114 changes\n 📝\u0000 hubspot_owner_id: 5 changes\n 📝\u0000 closedate: 97 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 pipeline: 2 changes\n 📝\u0000 hs_manual_forecast_category: 1 changes\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 50\n\n 🔔\u0000 Event: property_change\n Count: 314\n Properties:\n 📝\u0000 lastname: 53 changes\n 📝\u0000 email: 46 changes\n 📝\u0000 firstname: 58 changes\n 📝\u0000 mobilephone: 26 changes\n 📝\u0000 phone: 70 changes\n 📝\u0000 hubspot_owner_id: 59 changes\n 📝\u0000 associatedcompanyid: 2 changes\n\n 🔔\u0000 Event: association_change\n Count: 42\n\n\n INFO Looking for metrics: Config 1019 (SimpleConsign - 1088), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1019 (SimpleConsign - 1088)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 692\n\n 🔔\u0000 Event: property_change\n Count: 2659\n Properties:\n 📝\u0000 email: 334 changes\n 📝\u0000 hubspot_owner_id: 980 changes\n 📝\u0000 associatedcompanyid: 327 changes\n 📝\u0000 lastname: 332 changes\n 📝\u0000 phone: 20 changes\n 📝\u0000 firstname: 333 changes\n 📝\u0000 jobtitle: 321 changes\n 📝\u0000 mobilephone: 7 changes\n 📝\u0000 country: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 335\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 698\n\n 🔔\u0000 Event: property_change\n Count: 637\n Properties:\n 📝\u0000 domain: 310 changes\n 📝\u0000 name: 312 changes\n 📝\u0000 phone: 5 changes\n 📝\u0000 hubspot_owner_id: 4 changes\n 📝\u0000 country: 3 changes\n 📝\u0000 industry: 3 changes\n\n 🔔\u0000 Event: creation\n Count: 312\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 46\n\n 🔔\u0000 Event: creation\n Count: 9\n\n\n INFO Looking for metrics: Config 311 (Lemon.io - 329), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 311 (Lemon.io - 329)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 128\n Properties:\n 📝\u0000 domain: 26 changes\n 📝\u0000 name: 24 changes\n 📝\u0000 industry: 18 changes\n 📝\u0000 country: 17 changes\n 📝\u0000 hubspot_owner_id: 32 changes\n 📝\u0000 phone: 11 changes\n\n 🔔\u0000 Event: creation\n Count: 26\n\n 🔔\u0000 Event: association_change\n Count: 120\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 16\n\n 🔔\u0000 Event: property_change\n Count: 385\n Properties:\n 📝\u0000 closedate: 21 changes\n 📝\u0000 dealstage: 49 changes\n 📝\u0000 days_to_close: 19 changes\n 📝\u0000 hs_deal_stage_probability: 58 changes\n 📝\u0000 hs_manual_forecast_category: 34 changes\n 📝\u0000 hs_next_step: 14 changes\n 📝\u0000 level_of_involvement: 15 changes\n 📝\u0000 hubspot_owner_id: 20 changes\n 📝\u0000 freelancers_under_review: 31 changes\n 📝\u0000 pipeline: 8 changes\n 📝\u0000 dealname: 30 changes\n 📝\u0000 deal_currency_code: 8 changes\n 📝\u0000 amount: 13 changes\n 📝\u0000 hs_priority: 8 changes\n 📝\u0000 budget_limitations: 6 changes\n 📝\u0000 need: 7 changes\n 📝\u0000 positive_outcome: 6 changes\n 📝\u0000 competition: 6 changes\n 📝\u0000 negative_consequences: 6 changes\n 📝\u0000 selection_process: 8 changes\n 📝\u0000 project_team_structure: 7 changes\n 📝\u0000 champion: 6 changes\n 📝\u0000 red_flags: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 63\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 336\n Properties:\n 📝\u0000 hubspot_owner_id: 74 changes\n 📝\u0000 email: 56 changes\n 📝\u0000 firstname: 52 changes\n 📝\u0000 lastname: 48 changes\n 📝\u0000 country: 21 changes\n 📝\u0000 jobtitle: 26 changes\n 📝\u0000 associatedcompanyid: 39 changes\n 📝\u0000 phone: 15 changes\n 📝\u0000 mobilephone: 5 changes\n\n 🔔\u0000 Event: association_change\n Count: 103\n\n 🔔\u0000 Event: creation\n Count: 54\n\n\n INFO Looking for metrics: Config 802 (Street Group - 853), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 802 (Street Group - 853)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 252\n\n 🔔\u0000 Event: association_change\n Count: 558\n\n 🔔\u0000 Event: property_change\n Count: 1830\n Properties:\n 📝\u0000 phone: 170 changes\n 📝\u0000 lastname: 306 changes\n 📝\u0000 firstname: 318 changes\n 📝\u0000 email: 297 changes\n 📝\u0000 country: 287 changes\n 📝\u0000 jobtitle: 208 changes\n 📝\u0000 associatedcompanyid: 180 changes\n 📝\u0000 hubspot_owner_id: 52 changes\n 📝\u0000 mobilephone: 12 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 617\n\n 🔔\u0000 Event: creation\n Count: 31\n\n 🔔\u0000 Event: property_change\n Count: 94\n Properties:\n 📝\u0000 industry: 6 changes\n 📝\u0000 country: 10 changes\n 📝\u0000 phone: 10 changes\n 📝\u0000 name: 22 changes\n 📝\u0000 domain: 35 changes\n 📝\u0000 hubspot_owner_id: 11 changes\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: association_change\n Count: 121\n\n 🔔\u0000 Event: property_change\n Count: 338\n Properties:\n 📝\u0000 hs_deal_stage_probability: 78 changes\n 📝\u0000 deal_currency_code: 22 changes\n 📝\u0000 amount: 36 changes\n 📝\u0000 closedate: 42 changes\n 📝\u0000 dealstage: 91 changes\n 📝\u0000 dealtype: 2 changes\n 📝\u0000 hs_manual_forecast_category: 28 changes\n 📝\u0000 dealname: 8 changes\n 📝\u0000 hs_forecast_probability: 16 changes\n 📝\u0000 invoice_start_date: 8 changes\n 📝\u0000 hubspot_owner_id: 7 changes\n\n 🔔\u0000 Event: creation\n Count: 23\n\n\n INFO Looking for metrics: Config 1053 (Sensi.AI - 1117), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 1053 (Sensi.AI - 1117)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 156\n Properties:\n 📝\u0000 domain: 32 changes\n 📝\u0000 phone: 23 changes\n 📝\u0000 industry: 27 changes\n 📝\u0000 country: 29 changes\n 📝\u0000 name: 39 changes\n 📝\u0000 hubspot_owner_id: 6 changes\n\n 🔔\u0000 Event: creation\n Count: 39\n\n 🔔\u0000 Event: association_change\n Count: 1499\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 491\n Properties:\n 📝\u0000 hs_deal_stage_probability: 96 changes\n 📝\u0000 hs_next_step: 46 changes\n 📝\u0000 next_steps_jiminny: 38 changes\n 📝\u0000 hubspot_owner_id: 17 changes\n 📝\u0000 monthly_billable_hours: 10 changes\n 📝\u0000 closedate: 18 changes\n 📝\u0000 amount: 119 changes\n 📝\u0000 deal_currency_code: 115 changes\n 📝\u0000 dealstage: 23 changes\n 📝\u0000 dealname: 4 changes\n 📝\u0000 client_segmentation_jiminny: 1 changes\n 📝\u0000 pain_points__main_objections: 1 changes\n 📝\u0000 product_feedback: 1 changes\n 📝\u0000 red_flags: 1 changes\n 📝\u0000 competitors: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 98\n\n 🔔\u0000 Event: creation\n Count: 74\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 1577\n\n 🔔\u0000 Event: property_change\n Count: 3768\n Properties:\n 📝\u0000 email: 857 changes\n 📝\u0000 mobilephone: 104 changes\n 📝\u0000 lastname: 854 changes\n 📝\u0000 jobtitle: 98 changes\n 📝\u0000 phone: 130 changes\n 📝\u0000 firstname: 870 changes\n 📝\u0000 hubspot_owner_id: 92 changes\n 📝\u0000 associatedcompanyid: 737 changes\n 📝\u0000 country: 26 changes\n\n 🔔\u0000 Event: creation\n Count: 872\n\n\n INFO Looking for metrics: Config 87 (Repsly - 93), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 87 (Repsly - 93)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 295\n\n 🔔\u0000 Event: property_change\n Count: 1285\n Properties:\n 📝\u0000 lastname: 150 changes\n 📝\u0000 email: 128 changes\n 📝\u0000 firstname: 162 changes\n 📝\u0000 associatedcompanyid: 145 changes\n 📝\u0000 country: 130 changes\n 📝\u0000 hubspot_owner_id: 175 changes\n 📝\u0000 mobilephone: 203 changes\n 📝\u0000 phone: 60 changes\n 📝\u0000 jobtitle: 132 changes\n\n 🔔\u0000 Event: creation\n Count: 159\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: creation\n Count: 3\n\n 🔔\u0000 Event: association_change\n Count: 13\n\n 🔔\u0000 Event: property_change\n Count: 23\n Properties:\n 📝\u0000 hs_deal_stage_probability: 4 changes\n 📝\u0000 amount: 14 changes\n 📝\u0000 hubspot_owner_id: 1 changes\n 📝\u0000 closedate: 2 changes\n 📝\u0000 hs_next_step: 1 changes\n 📝\u0000 dealstage: 1 changes\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 298\n\n 🔔\u0000 Event: property_change\n Count: 23\n Properties:\n 📝\u0000 domain: 9 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 name: 6 changes\n 📝\u0000 hubspot_owner_id: 3 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 9\n\n\n INFO Looking for metrics: Config 518 (Prolific - 544), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 518 (Prolific - 544)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 14\n Properties:\n 📝\u0000 amount: 3 changes\n 📝\u0000 hs_deal_stage_probability: 4 changes\n 📝\u0000 dealname: 3 changes\n 📝\u0000 closedate: 3 changes\n 📝\u0000 dealstage: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 3\n\n 🔔\u0000 Event: association_change\n Count: 9\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 534\n\n 🔔\u0000 Event: property_change\n Count: 7533\n Properties:\n 📝\u0000 jobtitle: 73 changes\n 📝\u0000 phone: 2 changes\n 📝\u0000 email: 561 changes\n 📝\u0000 lastname: 81 changes\n 📝\u0000 firstname: 90 changes\n 📝\u0000 associatedcompanyid: 166 changes\n 📝\u0000 hubspot_owner_id: 6151 changes\n 📝\u0000 country: 409 changes\n\n 🔔\u0000 Event: association_change\n Count: 349\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: creation\n Count: 30\n\n 🔔\u0000 Event: association_change\n Count: 352\n\n 🔔\u0000 Event: property_change\n Count: 575\n Properties:\n 📝\u0000 domain: 30 changes\n 📝\u0000 country: 421 changes\n 📝\u0000 phone: 12 changes\n 📝\u0000 industry: 18 changes\n 📝\u0000 name: 23 changes\n 📝\u0000 hubspot_owner_id: 71 changes\n\n\n INFO Looking for metrics: Config 761 (Ressio Software - 770), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 761 (Ressio Software - 770)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 1370\n Properties:\n 📝\u0000 hubspot_owner_id: 471 changes\n 📝\u0000 domain: 199 changes\n 📝\u0000 name: 202 changes\n 📝\u0000 country: 163 changes\n 📝\u0000 industry: 170 changes\n 📝\u0000 phone: 165 changes\n\n 🔔\u0000 Event: creation\n Count: 207\n\n 🔔\u0000 Event: association_change\n Count: 435\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: property_change\n Count: 1582\n Properties:\n 📝\u0000 hubspot_owner_id: 476 changes\n 📝\u0000 phone: 155 changes\n 📝\u0000 mobilephone: 97 changes\n 📝\u0000 lastname: 153 changes\n 📝\u0000 email: 109 changes\n 📝\u0000 associatedcompanyid: 160 changes\n 📝\u0000 firstname: 163 changes\n 📝\u0000 jobtitle: 139 changes\n 📝\u0000 country: 130 changes\n\n 🔔\u0000 Event: creation\n Count: 152\n\n 🔔\u0000 Event: association_change\n Count: 386\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 119\n Properties:\n 📝\u0000 closedate: 16 changes\n 📝\u0000 dealstage: 36 changes\n 📝\u0000 hs_deal_stage_probability: 53 changes\n 📝\u0000 hubspot_owner_id: 9 changes\n 📝\u0000 amount: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 17\n\n 🔔\u0000 Event: association_change\n Count: 119\n\n\n INFO Looking for metrics: Config 537 (Mobiz - 563), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 537 (Mobiz - 563)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: creation\n Count: 19\n\n 🔔\u0000 Event: property_change\n Count: 751\n Properties:\n 📝\u0000 email: 26 changes\n 📝\u0000 lastname: 18 changes\n 📝\u0000 phone: 15 changes\n 📝\u0000 hubspot_owner_id: 630 changes\n 📝\u0000 firstname: 21 changes\n 📝\u0000 country: 9 changes\n 📝\u0000 jobtitle: 14 changes\n 📝\u0000 associatedcompanyid: 15 changes\n 📝\u0000 mobilephone: 3 changes\n\n 🔔\u0000 Event: association_change\n Count: 35\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: association_change\n Count: 36\n\n 🔔\u0000 Event: property_change\n Count: 24\n Properties:\n 📝\u0000 hubspot_owner_id: 6 changes\n 📝\u0000 domain: 5 changes\n 📝\u0000 country: 4 changes\n 📝\u0000 industry: 4 changes\n 📝\u0000 name: 4 changes\n 📝\u0000 phone: 1 changes\n\n 🔔\u0000 Event: creation\n Count: 4\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 1\n Properties:\n 📝\u0000 hs_deal_stage_probability: 1 changes\n\n 🔔\u0000 Event: association_change\n Count: 3\n\n 🔔\u0000 Event: creation\n Count: 1\n\n\n INFO Looking for metrics: Config 428 (Welcome to the Jungle UK - 461), Date 2026-04-16. \n\n📊\u0000 Webhook Metrics for Config 428 (Welcome to the Jungle UK - 461)\n==========================================\nDate: 2026-04-16\n\n 📦\u0000 Object Type: contact\n 🔔\u0000 Event: association_change\n Count: 107\n\n 🔔\u0000 Event: property_change\n Count: 352\n Properties:\n 📝\u0000 firstname: 34 changes\n 📝\u0000 email: 35 changes\n 📝\u0000 lastname: 32 changes\n 📝\u0000 associatedcompanyid: 37 changes\n 📝\u0000 country: 29 changes\n 📝\u0000 jobtitle: 29 changes\n 📝\u0000 phone: 43 changes\n 📝\u0000 hubspot_owner_id: 108 changes\n 📝\u0000 mobilephone: 5 changes\n\n 🔔\u0000 Event: creation\n Count: 32\n\n 📦\u0000 Object Type: deal\n 🔔\u0000 Event: property_change\n Count: 225\n Properties:\n 📝\u0000 hs_deal_stage_probability: 53 changes\n 📝\u0000 product: 7 changes\n 📝\u0000 closedate: 20 changes\n 📝\u0000 dealstage: 40 changes\n 📝\u0000 hs_manual_forecast_category: 31 changes\n 📝\u0000 amount: 26 changes\n 📝\u0000 deal_currency_code: 17 changes\n 📝\u0000 dealname: 5 changes\n 📝\u0000 segment: 16 changes\n 📝\u0000 hs_next_step: 6 changes\n 📝\u0000 deal_source: 4 changes\n\n 🔔\u0000 Event: creation\n Count: 14\n\n 🔔\u0000 Event: association_change\n Count: 41\n\n 📦\u0000 Object Type: company\n 🔔\u0000 Event: property_change\n Count: 34\n Properties:\n 📝\u0000 hubspot_owner_id: 18 changes\n 📝\u0000 domain: 9 changes\n 📝\u0000 name: 7 changes\n\n 🔔\u0000 Event: creation\n Count: 9\n\n 🔔\u0000 Event: association_change\n Count: 130\n\n\n INFO Looking for metrics: Config 581 (Penfold - 606), Date 2026-04-16.","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.12291667,"top":0.05888889,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.12708333,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.24583334,"top":0.05888889,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.25,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Build full day activity summary from Screenpipe (claude)","depth":2,"bounds":{"left":0.36875,"top":0.05888889,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.37291667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.49166667,"top":0.05888889,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49583334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.6145833,"top":0.05888889,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.61875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.7375,"top":0.05888889,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.7416667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"ec2-user@ip-10-30-159-186:~ (nc)","depth":2,"bounds":{"left":0.86041665,"top":0.05888889,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.8645833,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"ec2-user@ip-10-30-159-186:~","depth":1,"bounds":{"left":0.42847222,"top":0.033333335,"width":0.14305556,"height":0.017777778},"role_description":"text"}]...
|
-6916705689786174882
|
7901245624159709559
|
idle
|
accessibility
|
NULL
|
📝 jobtitle: 2 changes
🔔 Event: creation
📝 jobtitle: 2 changes
🔔 Event: creation
Count: 82
📦 Object Type: deal
🔔 Event: creation
Count: 28
🔔 Event: property_change
Count: 153
Properties:
📝 dealstage: 48 changes
📝 hs_deal_stage_probability: 59 changes
📝 deal_currency_code: 3 changes
📝 amount: 3 changes
📝 closedate: 11 changes
📝 dealtype: 28 changes
📝 dealname: 1 changes
🔔 Event: association_change
Count: 85
📦 Object Type: company
🔔 Event: association_change
Count: 86
🔔 Event: property_change
Count: 11
Properties:
📝 domain: 6 changes
📝 name: 4 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: creation
Count: 4
INFO Looking for metrics: Config 801 (Rise Vision - 852), Date 2026-04-16.
📊 Webhook Metrics for Config 801 (Rise Vision - 852)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: association_change
Count: 57
🔔 Event: property_change
Count: 117
Properties:
📝 closedate: 20 changes
📝 dealstage: 10 changes
📝 hs_deal_stage_probability: 25 changes
📝 hs_manual_forecast_category: 25 changes
📝 amount: 29 changes
📝 dealname: 5 changes
📝 pipeline: 2 changes
📝 hs_next_step: 1 changes
🔔 Event: creation
Count: 16
📦 Object Type: company
🔔 Event: association_change
Count: 149
🔔 Event: creation
Count: 26
🔔 Event: property_change
Count: 73
Properties:
📝 industry: 26 changes
📝 country: 19 changes
📝 name: 26 changes
📝 phone: 1 changes
📝 domain: 1 changes
📦 Object Type: contact
🔔 Event: creation
Count: 95
🔔 Event: association_change
Count: 150
🔔 Event: property_change
Count: 418
Properties:
📝 email: 93 changes
📝 firstname: 90 changes
📝 lastname: 87 changes
📝 associatedcompanyid: 46 changes
📝 phone: 31 changes
📝 jobtitle: 21 changes
📝 hubspot_owner_id: 37 changes
📝 mobilephone: 3 changes
📝 country: 10 changes
INFO Looking for metrics: Config 834 (AnyVan.com - 882), Date 2026-04-16.
📊 Webhook Metrics for Config 834 (AnyVan.com - 882)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 5687
Properties:
📝 name: 4629 changes
📝 domain: 154 changes
📝 hubspot_owner_id: 904 changes
🔔 Event: association_change
Count: 25219
🔔 Event: creation
Count: 4689
📦 Object Type: contact
🔔 Event: property_change
Count: 54620
Properties:
📝 hubspot_owner_id: 2837 changes
📝 email: 4471 changes
📝 phone: 3661 changes
📝 lastname: 16150 changes
📝 firstname: 22622 changes
📝 associatedcompanyid: 4860 changes
📝 mobilephone: 13 changes
📝 jobtitle: 5 changes
📝 country: 1 changes
🔔 Event: creation
Count: 4471
🔔 Event: association_change
Count: 17648
📦 Object Type: deal
🔔 Event: creation
Count: 7588
🔔 Event: property_change
Count: 82720
Properties:
📝 dealname: 4204 changes
📝 amount: 22594 changes
📝 hs_deal_stage_probability: 17646 changes
📝 pipeline: 4356 changes
📝 dealstage: 14626 changes
📝 closedate: 11096 changes
📝 hubspot_owner_id: 6094 changes
📝 selected_date: 1969 changes
📝 deal_currency_code: 135 changes
🔔 Event: association_change
Count: 22773
INFO Looking for metrics: Config 878 (Dingus and Zazzy - 929), Date 2026-04-16.
📊 Webhook Metrics for Config 878 (Dingus and Zazzy - 929)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: property_change
Count: 158
Properties:
📝 amount_in_home_currency: 24 changes
📝 hs_deal_stage_probability: 35 changes
📝 hs_manual_forecast_category: 27 changes
📝 closedate: 23 changes
📝 lost_reason: 9 changes
📝 dealstage: 27 changes
📝 dealname: 9 changes
📝 hubspot_owner_id: 2 changes
📝 source_attribution: 2 changes
🔔 Event: creation
Count: 18
🔔 Event: association_change
Count: 52
📦 Object Type: company
🔔 Event: property_change
Count: 59
Properties:
📝 domain: 14 changes
📝 name: 14 changes
📝 hubspot_owner_id: 17 changes
📝 industry: 5 changes
📝 phone: 4 changes
📝 country: 5 changes
🔔 Event: association_change
Count: 69
🔔 Event: creation
Count: 14
📦 Object Type: contact
🔔 Event: property_change
Count: 192
Properties:
📝 phone: 8 changes
📝 lastname: 22 changes
📝 firstname: 22 changes
📝 jobtitle: 20 changes
📝 hubspot_owner_id: 76 changes
📝 email: 17 changes
📝 associatedcompanyid: 16 changes
📝 mobilephone: 11 changes
🔔 Event: association_change
Count: 53
🔔 Event: creation
Count: 20
INFO Looking for metrics: Config 671 (CosmosID - 691), Date 2026-04-16.
📊 Webhook Metrics for Config 671 (CosmosID - 691)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: creation
Count: 5
🔔 Event: property_change
Count: 15
Properties:
📝 hubspot_owner_id: 6 changes
📝 name: 5 changes
📝 domain: 3 changes
📝 phone: 1 changes
🔔 Event: association_change
Count: 44
📦 Object Type: deal
🔔 Event: property_change
Count: 78
Properties:
📝 hs_deal_stage_probability: 14 changes
📝 createdate: 10 changes
📝 hs_manual_forecast_category: 13 changes
📝 deal_currency_code: 4 changes
📝 amount: 20 changes
📝 closedate: 8 changes
📝 dealstage: 4 changes
📝 dealname: 3 changes
📝 dealtype: 1 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: association_change
Count: 30
🔔 Event: creation
Count: 10
📦 Object Type: contact
🔔 Event: creation
Count: 13
🔔 Event: property_change
Count: 145
Properties:
📝 jobtitle: 77 changes
📝 hubspot_owner_id: 12 changes
📝 firstname: 9 changes
📝 phone: 5 changes
📝 lastname: 8 changes
📝 email: 13 changes
📝 associatedcompanyid: 12 changes
📝 country: 9 changes
🔔 Event: association_change
Count: 34
INFO Looking for metrics: Config 652 (Abode - 673), Date 2026-04-16.
📊 Webhook Metrics for Config 652 (Abode - 673)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 1
Properties:
📝 country: 1 changes
🔔 Event: association_change
Count: 10
📦 Object Type: contact
🔔 Event: creation
Count: 6
🔔 Event: property_change
Count: 40
Properties:
📝 phone: 3 changes
📝 hubspot_owner_id: 8 changes
📝 email: 6 changes
📝 associatedcompanyid: 4 changes
📝 country: 4 changes
📝 lastname: 5 changes
📝 jobtitle: 4 changes
📝 firstname: 5 changes
📝 mobilephone: 1 changes
🔔 Event: association_change
Count: 9
📦 Object Type: deal
🔔 Event: creation
Count: 1
🔔 Event: association_change
Count: 3
🔔 Event: property_change
Count: 13
Properties:
📝 dealstage: 4 changes
📝 hs_deal_stage_probability: 5 changes
📝 dealname: 1 changes
📝 closedate: 1 changes
📝 hs_manual_forecast_category: 1 changes
📝 amount: 1 changes
INFO Looking for metrics: Config 1049 (Classavo - 851), Date 2026-04-16.
📊 Webhook Metrics for Config 1049 (Classavo - 851)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: association_change
Count: 2
📦 Object Type: contact
🔔 Event: association_change
Count: 1
🔔 Event: property_change
Count: 3
Properties:
📝 firstname: 1 changes
📝 lastname: 1 changes
📝 jobtitle: 1 changes
📦 Object Type: deal
🔔 Event: association_change
Count: 3
🔔 Event: property_change
Count: 14
Properties:
📝 dealstage: 4 changes
📝 hs_deal_stage_probability: 5 changes
📝 closedate: 3 changes
📝 deal_currency_code: 1 changes
📝 amount: 1 changes
🔔 Event: creation
Count: 1
INFO Looking for metrics: Config 290 (D1 Training - 308), Date 2026-04-16.
📊 Webhook Metrics for Config 290 (D1 Training - 308)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 5
Properties:
📝 name: 4 changes
📝 hubspot_owner_id: 1 changes
🔔 Event: creation
Count: 1
🔔 Event: association_change
Count: 6
📦 Object Type: deal
🔔 Event: association_change
Count: 40
🔔 Event: creation
Count: 35
🔔 Event: property_change
Count: 405
Properties:
📝 hs_deal_stage_probability: 149 changes
📝 dealtype: 34 changes
📝 dealstage: 114 changes
📝 hubspot_owner_id: 5 changes
📝 closedate: 97 changes
📝 dealname: 3 changes
📝 pipeline: 2 changes
📝 hs_manual_forecast_category: 1 changes
📦 Object Type: contact
🔔 Event: creation
Count: 50
🔔 Event: property_change
Count: 314
Properties:
📝 lastname: 53 changes
📝 email: 46 changes
📝 firstname: 58 changes
📝 mobilephone: 26 changes
📝 phone: 70 changes
📝 hubspot_owner_id: 59 changes
📝 associatedcompanyid: 2 changes
🔔 Event: association_change
Count: 42
INFO Looking for metrics: Config 1019 (SimpleConsign - 1088), Date 2026-04-16.
📊 Webhook Metrics for Config 1019 (SimpleConsign - 1088)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 692
🔔 Event: property_change
Count: 2659
Properties:
📝 email: 334 changes
📝 hubspot_owner_id: 980 changes
📝 associatedcompanyid: 327 changes
📝 lastname: 332 changes
📝 phone: 20 changes
📝 firstname: 333 changes
📝 jobtitle: 321 changes
📝 mobilephone: 7 changes
📝 country: 5 changes
🔔 Event: creation
Count: 335
📦 Object Type: company
🔔 Event: association_change
Count: 698
🔔 Event: property_change
Count: 637
Properties:
📝 domain: 310 changes
📝 name: 312 changes
📝 phone: 5 changes
📝 hubspot_owner_id: 4 changes
📝 country: 3 changes
📝 industry: 3 changes
🔔 Event: creation
Count: 312
📦 Object Type: deal
🔔 Event: association_change
Count: 46
🔔 Event: creation
Count: 9
INFO Looking for metrics: Config 311 (Lemon.io - 329), Date 2026-04-16.
📊 Webhook Metrics for Config 311 (Lemon.io - 329)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 128
Properties:
📝 domain: 26 changes
📝 name: 24 changes
📝 industry: 18 changes
📝 country: 17 changes
📝 hubspot_owner_id: 32 changes
📝 phone: 11 changes
🔔 Event: creation
Count: 26
🔔 Event: association_change
Count: 120
📦 Object Type: deal
🔔 Event: creation
Count: 16
🔔 Event: property_change
Count: 385
Properties:
📝 closedate: 21 changes
📝 dealstage: 49 changes
📝 days_to_close: 19 changes
📝 hs_deal_stage_probability: 58 changes
📝 hs_manual_forecast_category: 34 changes
📝 hs_next_step: 14 changes
📝 level_of_involvement: 15 changes
📝 hubspot_owner_id: 20 changes
📝 freelancers_under_review: 31 changes
📝 pipeline: 8 changes
📝 dealname: 30 changes
📝 deal_currency_code: 8 changes
📝 amount: 13 changes
📝 hs_priority: 8 changes
📝 budget_limitations: 6 changes
📝 need: 7 changes
📝 positive_outcome: 6 changes
📝 competition: 6 changes
📝 negative_consequences: 6 changes
📝 selection_process: 8 changes
📝 project_team_structure: 7 changes
📝 champion: 6 changes
📝 red_flags: 5 changes
🔔 Event: association_change
Count: 63
📦 Object Type: contact
🔔 Event: property_change
Count: 336
Properties:
📝 hubspot_owner_id: 74 changes
📝 email: 56 changes
📝 firstname: 52 changes
📝 lastname: 48 changes
📝 country: 21 changes
📝 jobtitle: 26 changes
📝 associatedcompanyid: 39 changes
📝 phone: 15 changes
📝 mobilephone: 5 changes
🔔 Event: association_change
Count: 103
🔔 Event: creation
Count: 54
INFO Looking for metrics: Config 802 (Street Group - 853), Date 2026-04-16.
📊 Webhook Metrics for Config 802 (Street Group - 853)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: creation
Count: 252
🔔 Event: association_change
Count: 558
🔔 Event: property_change
Count: 1830
Properties:
📝 phone: 170 changes
📝 lastname: 306 changes
📝 firstname: 318 changes
📝 email: 297 changes
📝 country: 287 changes
📝 jobtitle: 208 changes
📝 associatedcompanyid: 180 changes
📝 hubspot_owner_id: 52 changes
📝 mobilephone: 12 changes
📦 Object Type: company
🔔 Event: association_change
Count: 617
🔔 Event: creation
Count: 31
🔔 Event: property_change
Count: 94
Properties:
📝 industry: 6 changes
📝 country: 10 changes
📝 phone: 10 changes
📝 name: 22 changes
📝 domain: 35 changes
📝 hubspot_owner_id: 11 changes
📦 Object Type: deal
🔔 Event: association_change
Count: 121
🔔 Event: property_change
Count: 338
Properties:
📝 hs_deal_stage_probability: 78 changes
📝 deal_currency_code: 22 changes
📝 amount: 36 changes
📝 closedate: 42 changes
📝 dealstage: 91 changes
📝 dealtype: 2 changes
📝 hs_manual_forecast_category: 28 changes
📝 dealname: 8 changes
📝 hs_forecast_probability: 16 changes
📝 invoice_start_date: 8 changes
📝 hubspot_owner_id: 7 changes
🔔 Event: creation
Count: 23
INFO Looking for metrics: Config 1053 (Sensi.AI - 1117), Date 2026-04-16.
📊 Webhook Metrics for Config 1053 (Sensi.AI - 1117)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 156
Properties:
📝 domain: 32 changes
📝 phone: 23 changes
📝 industry: 27 changes
📝 country: 29 changes
📝 name: 39 changes
📝 hubspot_owner_id: 6 changes
🔔 Event: creation
Count: 39
🔔 Event: association_change
Count: 1499
📦 Object Type: deal
🔔 Event: property_change
Count: 491
Properties:
📝 hs_deal_stage_probability: 96 changes
📝 hs_next_step: 46 changes
📝 next_steps_jiminny: 38 changes
📝 hubspot_owner_id: 17 changes
📝 monthly_billable_hours: 10 changes
📝 closedate: 18 changes
📝 amount: 119 changes
📝 deal_currency_code: 115 changes
📝 dealstage: 23 changes
📝 dealname: 4 changes
📝 client_segmentation_jiminny: 1 changes
📝 pain_points__main_objections: 1 changes
📝 product_feedback: 1 changes
📝 red_flags: 1 changes
📝 competitors: 1 changes
🔔 Event: association_change
Count: 98
🔔 Event: creation
Count: 74
📦 Object Type: contact
🔔 Event: association_change
Count: 1577
🔔 Event: property_change
Count: 3768
Properties:
📝 email: 857 changes
📝 mobilephone: 104 changes
📝 lastname: 854 changes
📝 jobtitle: 98 changes
📝 phone: 130 changes
📝 firstname: 870 changes
📝 hubspot_owner_id: 92 changes
📝 associatedcompanyid: 737 changes
📝 country: 26 changes
🔔 Event: creation
Count: 872
INFO Looking for metrics: Config 87 (Repsly - 93), Date 2026-04-16.
📊 Webhook Metrics for Config 87 (Repsly - 93)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 295
🔔 Event: property_change
Count: 1285
Properties:
📝 lastname: 150 changes
📝 email: 128 changes
📝 firstname: 162 changes
📝 associatedcompanyid: 145 changes
📝 country: 130 changes
📝 hubspot_owner_id: 175 changes
📝 mobilephone: 203 changes
📝 phone: 60 changes
📝 jobtitle: 132 changes
🔔 Event: creation
Count: 159
📦 Object Type: deal
🔔 Event: creation
Count: 3
🔔 Event: association_change
Count: 13
🔔 Event: property_change
Count: 23
Properties:
📝 hs_deal_stage_probability: 4 changes
📝 amount: 14 changes
📝 hubspot_owner_id: 1 changes
📝 closedate: 2 changes
📝 hs_next_step: 1 changes
📝 dealstage: 1 changes
📦 Object Type: company
🔔 Event: association_change
Count: 298
🔔 Event: property_change
Count: 23
Properties:
📝 domain: 9 changes
📝 country: 4 changes
📝 name: 6 changes
📝 hubspot_owner_id: 3 changes
📝 phone: 1 changes
🔔 Event: creation
Count: 9
INFO Looking for metrics: Config 518 (Prolific - 544), Date 2026-04-16.
📊 Webhook Metrics for Config 518 (Prolific - 544)
==========================================
Date: 2026-04-16
📦 Object Type: deal
🔔 Event: property_change
Count: 14
Properties:
📝 amount: 3 changes
📝 hs_deal_stage_probability: 4 changes
📝 dealname: 3 changes
📝 closedate: 3 changes
📝 dealstage: 1 changes
🔔 Event: creation
Count: 3
🔔 Event: association_change
Count: 9
📦 Object Type: contact
🔔 Event: creation
Count: 534
🔔 Event: property_change
Count: 7533
Properties:
📝 jobtitle: 73 changes
📝 phone: 2 changes
📝 email: 561 changes
📝 lastname: 81 changes
📝 firstname: 90 changes
📝 associatedcompanyid: 166 changes
📝 hubspot_owner_id: 6151 changes
📝 country: 409 changes
🔔 Event: association_change
Count: 349
📦 Object Type: company
🔔 Event: creation
Count: 30
🔔 Event: association_change
Count: 352
🔔 Event: property_change
Count: 575
Properties:
📝 domain: 30 changes
📝 country: 421 changes
📝 phone: 12 changes
📝 industry: 18 changes
📝 name: 23 changes
📝 hubspot_owner_id: 71 changes
INFO Looking for metrics: Config 761 (Ressio Software - 770), Date 2026-04-16.
📊 Webhook Metrics for Config 761 (Ressio Software - 770)
==========================================
Date: 2026-04-16
📦 Object Type: company
🔔 Event: property_change
Count: 1370
Properties:
📝 hubspot_owner_id: 471 changes
📝 domain: 199 changes
📝 name: 202 changes
📝 country: 163 changes
📝 industry: 170 changes
📝 phone: 165 changes
🔔 Event: creation
Count: 207
🔔 Event: association_change
Count: 435
📦 Object Type: contact
🔔 Event: property_change
Count: 1582
Properties:
📝 hubspot_owner_id: 476 changes
📝 phone: 155 changes
📝 mobilephone: 97 changes
📝 lastname: 153 changes
📝 email: 109 changes
📝 associatedcompanyid: 160 changes
📝 firstname: 163 changes
📝 jobtitle: 139 changes
📝 country: 130 changes
🔔 Event: creation
Count: 152
🔔 Event: association_change
Count: 386
📦 Object Type: deal
🔔 Event: property_change
Count: 119
Properties:
📝 closedate: 16 changes
📝 dealstage: 36 changes
📝 hs_deal_stage_probability: 53 changes
📝 hubspot_owner_id: 9 changes
📝 amount: 5 changes
🔔 Event: creation
Count: 17
🔔 Event: association_change
Count: 119
INFO Looking for metrics: Config 537 (Mobiz - 563), Date 2026-04-16.
📊 Webhook Metrics for Config 537 (Mobiz - 563)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: creation
Count: 19
🔔 Event: property_change
Count: 751
Properties:
📝 email: 26 changes
📝 lastname: 18 changes
📝 phone: 15 changes
📝 hubspot_owner_id: 630 changes
📝 firstname: 21 changes
📝 country: 9 changes
📝 jobtitle: 14 changes
📝 associatedcompanyid: 15 changes
📝 mobilephone: 3 changes
🔔 Event: association_change
Count: 35
📦 Object Type: company
🔔 Event: association_change
Count: 36
🔔 Event: property_change
Count: 24
Properties:
📝 hubspot_owner_id: 6 changes
📝 domain: 5 changes
📝 country: 4 changes
📝 industry: 4 changes
📝 name: 4 changes
📝 phone: 1 changes
🔔 Event: creation
Count: 4
📦 Object Type: deal
🔔 Event: property_change
Count: 1
Properties:
📝 hs_deal_stage_probability: 1 changes
🔔 Event: association_change
Count: 3
🔔 Event: creation
Count: 1
INFO Looking for metrics: Config 428 (Welcome to the Jungle UK - 461), Date 2026-04-16.
📊 Webhook Metrics for Config 428 (Welcome to the Jungle UK - 461)
==========================================
Date: 2026-04-16
📦 Object Type: contact
🔔 Event: association_change
Count: 107
🔔 Event: property_change
Count: 352
Properties:
📝 firstname: 34 changes
📝 email: 35 changes
📝 lastname: 32 changes
📝 associatedcompanyid: 37 changes
📝 country: 29 changes
📝 jobtitle: 29 changes
📝 phone: 43 changes
📝 hubspot_owner_id: 108 changes
📝 mobilephone: 5 changes
🔔 Event: creation
Count: 32
📦 Object Type: deal
🔔 Event: property_change
Count: 225
Properties:
📝 hs_deal_stage_probability: 53 changes
📝 product: 7 changes
📝 closedate: 20 changes
📝 dealstage: 40 changes
📝 hs_manual_forecast_category: 31 changes
📝 amount: 26 changes
📝 deal_currency_code: 17 changes
📝 dealname: 5 changes
📝 segment: 16 changes
📝 hs_next_step: 6 changes
📝 deal_source: 4 changes
🔔 Event: creation
Count: 14
🔔 Event: association_change
Count: 41
📦 Object Type: company
🔔 Event: property_change
Count: 34
Properties:
📝 hubspot_owner_id: 18 changes
📝 domain: 9 changes
📝 name: 7 changes
🔔 Event: creation
Count: 9
🔔 Event: association_change
Count: 130
INFO Looking for metrics: Config 581 (Penfold - 606), Date 2026-04-16.
DOCKER
Close Tab
-zsh
Close Tab
-zsh
Close Tab
✳ Build full day activity summary from Screenpipe (claude)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
APP (-zsh)
Close Tab
ec2-user@ip-10-30-159-186:~ (nc)
Close Tab
⌥⌘1
ec2-user@ip-10-30-159-186:~...
|
69500
|
|
39657
|
806
|
10
|
2026-04-16T13:57:11.783701+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776347831783_m1.jpg...
|
iTerm2
|
curl
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
curl
Close Tab
⌥⌘1
curl...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.196875,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.20104167,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.39375,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.39791667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5902778,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.59444445,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"curl","depth":2,"bounds":{"left":0.78680557,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.79097223,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"curl","depth":1,"bounds":{"left":0.48958334,"top":0.033333335,"width":0.02013889,"height":0.017777778},"role_description":"text"}]...
|
-2754766195111425292
|
7900608259477237228
|
visual_change
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
curl
Close Tab
⌥⌘1
curl...
|
39655
|
|
39658
|
806
|
11
|
2026-04-16T13:57:17.833155+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776347837833_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.196875,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.20104167,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.39375,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.39791667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5902778,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.59444445,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.78680557,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.79097223,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.48819444,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
-6216735170207247706
|
7899482358496652780
|
visual_change
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
NULL
|
|
39659
|
807
|
10
|
2026-04-16T13:57:31.768389+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776347851768_m2.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.23320313,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.23554687,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.34394532,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.34628907,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.4546875,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.45703125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.56523436,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.56757814,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.67578125,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.678125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.7703125,"top":1.0,"width":0.021875,"height":-0.02013886},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.5078125,"top":1.0,"width":0.012890625,"height":-0.020833373},"role_description":"text"}]...
|
-6216735170207247706
|
7899482358496652780
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
NULL
|
|
39828
|
812
|
9
|
2026-04-16T14:12:15.642219+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776348735642_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.196875,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.20104167,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.39375,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.39791667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5902778,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.59444445,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.78680557,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.79097223,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.48819444,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
-6216735170207247706
|
7899482358496652780
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
39827
|
|
39829
|
813
|
7
|
2026-04-16T14:12:15.656840+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776348735656_m2.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.23320313,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.23554687,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.34394532,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.34628907,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.4546875,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.45703125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.56523436,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.56757814,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.67578125,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.678125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.7703125,"top":1.0,"width":0.021875,"height":-0.02013886},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.5078125,"top":1.0,"width":0.012890625,"height":-0.020833373},"role_description":"text"}]...
|
-6216735170207247706
|
7899482358496652780
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
39826
|
|
39903
|
816
|
6
|
2026-04-16T14:18:20.954078+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776349100954_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.196875,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.20104167,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.39375,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.39791667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5902778,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.59444445,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.78680557,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.79097223,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.48819444,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
-6216735170207247706
|
7899482358496652780
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
NULL
|
|
39904
|
817
|
6
|
2026-04-16T14:18:20.978738+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776349100978_m2.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.23320313,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.23554687,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.34394532,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.34628907,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.4546875,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.45703125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.56523436,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.56757814,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.67578125,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.678125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.7703125,"top":1.0,"width":0.021875,"height":-0.02013886},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.5078125,"top":1.0,"width":0.012890625,"height":-0.020833373},"role_description":"text"}]...
|
-6216735170207247706
|
7899482358496652780
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
39902
|
|
39905
|
816
|
7
|
2026-04-16T14:18:21.737020+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776349101737_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.196875,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.20104167,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.39375,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.39791667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5902778,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.59444445,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.78680557,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.79097223,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.48819444,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
-6216735170207247706
|
7899482358496652780
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
NULL
|
|
39906
|
817
|
7
|
2026-04-16T14:18:21.721513+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776349101721_m2.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.23320313,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.23554687,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.34394532,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.34628907,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.4546875,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.45703125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.56523436,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.56757814,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.67578125,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.678125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.7703125,"top":1.0,"width":0.021875,"height":-0.02013886},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.5078125,"top":1.0,"width":0.012890625,"height":-0.020833373},"role_description":"text"}]...
|
-6216735170207247706
|
7899482358496652780
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
NULL
|
|
4129
|
80
|
60
|
2026-04-12T19:54:41.901832+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-12/1776 /Users/lukas/.screenpipe/data/data/2026-04-12/1776023681901_m1.jpg...
|
Firefox
|
DXP4800PLUS-B5F8 — Personal
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Melania
Loading
00:26:32
01:44:04
Play
Melania
Loading
00:26:32
01:44:04
Play
Original
1.0X
...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Melania","depth":14,"bounds":{"left":0.055555556,"top":0.01,"width":0.03888889,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Loading","depth":13,"bounds":{"left":0.48020834,"top":0.5038889,"width":0.039583333,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"00:26:32","depth":13,"bounds":{"left":0.016666668,"top":0.9116667,"width":0.03784722,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"01:44:04","depth":13,"bounds":{"left":0.9454861,"top":0.9116667,"width":0.03784722,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.015277778,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.048611112,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.08194444,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Play","depth":14,"bounds":{"left":0.08229167,"top":0.8811111,"width":0.016319444,"height":0.015555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.115277775,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.14861111,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":13,"bounds":{"left":0.73888886,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Original","depth":12,"bounds":{"left":0.7722222,"top":0.95666665,"width":0.038194444,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1.0X","depth":12,"bounds":{"left":0.82708335,"top":0.95611113,"width":0.022916667,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":12,"bounds":{"left":0.8666667,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.9,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.93333334,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":14,"bounds":{"left":0.96666664,"top":0.9527778,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":11,"bounds":{"left":0.4861111,"top":0.47777778,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8379339344493791060
|
7898516134753986564
|
click
|
hybrid
|
NULL
|
Melania
Loading
00:26:32
01:44:04
Play
Melania
Loading
00:26:32
01:44:04
Play
Original
1.0X
Activity MonitorFileEditViewWindowHelpDOCKER₫12026-04-12122:20:29699327Z2026-04-12T22:20:34.005845Z2026-04--12T22:20:35..64882722026-04-12T22:20:38.768848Z2026-04-12T22:20:47878009Z2026-04-12T22:20:50..900875Z2026-04-12T22:20:59937401Z2026-04-12T22:21:27.187255Z2026-04-12T22:21:30.2026-04-12T22:21:33.214215Z220423Z2026-04-12T22:21:39027483Z2026-04--12T22:21:54.477609Z2026-04-12T22:21:57490028Z2026-04-12T22:22:06.557939Z2026-04-12T22:22:09561968Z2026-04--12T22:22:12..620628Z2026-04-12T22:22:27.699475Z2026-04-12T22:22:39836999Z2026-04-12T22:22:44.052745Z2026-04-12T22:22:54.977274Z2026-04-12T22:23:04.067624Z2026-04-12T22:23:22.229817Z2026-04-12T22:23:28.278077Z2026-04-12T22:23:34.312538Z2026-04-12T22:23:40.338438Z2026-04-12T22:23:43350979Z2026-04-12T22:23:49082693Z2026-04-12T22:23:58.499624Z2026-04-12T22:24:01.517862Z2026-04-12T22:24:10..610720Z2026-04-12T22:24:13.622839Z2026-04-12T22:24:28.729718Z2026-04-12T22:24:37.819714Z2026-04-12T22:24:40.807487Z2026-04-12T22:24:43.926970Z2026-04-12T22:24:54.102622Z2026-04-12T22:24:56.037518Z2026-04-12T22:25:04.994185ZDEV (-zsh)₴2APP (-zsh)83-zsh• 84|INFOscreenpipe_engine::event_driven_capture:contentdedup:WARNINFOscreenpipe_engine::resource_monitor: PostHog request tiscreenpipe_engine::retention:retention:cleaning updaINFOscreenpipe_engine::event_driven.capture:content dedup:INFOscreenpipe_engine::event_driven.capture:contentdedup:INFOscreenpipe_engine::event_driven.capture:contentdedup:INFOscreenpipe_engine::event_driven_capture:contentdedup:INFOscreenpipe_engine::event_driven_capture:contentdedup:INFOscreenpipe_engine::event_driven_capture:contentdedup:INFOscreenpipe_engine::event_driven._capture:contentdedup:WARNscreenpipe_engine::resource_monitor:PostHog requesttiINFOscreenpipe_engine::event_driven.capture:contentdedup:INFOscreenpipe_engine::event_drivencapture:contentdedup:INFOscreenpipe_engine::event_driven.capture:contentdedup:INFOINFOscreenpipe_engine::event_driven_capture:contentdedup:screenpipe_engine::event_driven.capture:contentdedup:INFOscreenpipe_engine::event_drivencapture:contentdedup:INFOscreenpipe_engine::event_driven.capture:contentdedup:WARNscreenpipe_engine::resource_monitor: PostHog requestINFOscreenpipe_engine::event_driven_capture:content dedup:INFOscreenpipe_engine::event_driven_capture:content dedup:INFOINFOscreenpipe_engine::event_driven_capture:content dedup:screenpipe_engine::event_driven_capture:content dedup:INFOscreenpipe_engine::event_driven_capture:content dedup:INFOscreenpipe_engine::event_driven.capture:contentINFOdedup:screenpipe_engine::event_driven.capture:contentdedup:WARNscreenpipe_engine::resource_monitor: PostHog request tiINFOscreenpipe_engine::event_driven_capture:contentdedup:INFOscreenpipe_engine::event_driven_capture:contentINFOscreenpipe_engine::event_driven_capture:dedup:contentdedup:INFOscreenpipe_engine:: event_driven.capture:contentdedup:INFOINFOscreenpipe_engine::event_driven_capture:contentdedup:screenpipe_engine::event_driven_capture:contentdedup:INFOscreenpipe_engine::event_driven_capture:contentdedup:INFOWARNscreenpipe_engine::event_driven_capture:contentdedup:screenpipe_engine::resource_monitor: PostHog request tiINFOscreenpipe_engine::event_driven_capture:content dedup:INFOscreenpipe_engine::event_driven_capture:content dedup:lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ 2026-04-12T22:25:14.04itor 1(hash=-6660592639383128564,trigger=visual_change)2026-04-12T22:25:20.906940ZINFO2026-04-12T22:25:35.646197ZINFOscreenpipe_engine::event_driven_capture:content dedup:screenpipe_engine::retention: retention: cleaning upda2026-04-12T22:25:59.131708ZWARNscreenpipe_engine::resource_monitor:PostHog requesttiActivityAll ProcessProc$1iTermCurrently SharingHUGIUtipsdfamilycircleditunesclouddiCloudNotificationtalagentdiagnostics_agentassistant_servicecontactsdonationasiriactionsdakdamsaccountsdcom.apple.geoddasdSiriNCServicefmfdCoreLocationAgentaudioaccessorydcontainermanagerd_systemcontextstoredzshbirdKeychain Circle NotificationCommCenteranalyticsd1Password HelpermediaanalysisdClaude HelpernsurlsessiondMEMORY PRESSUREStop Sharing6,8 MB6,8 MB6,8 MB6,7 MB6,6 MB6,4 MB6,4 MB6,4 MB6,4 MB6,4 MB6,3 MB6,3 MB6,3 MB6,2 MB6,2 MB6,2 MB6,1 MB6,1 MBPhysical Memory:Memory Used:Cached Files:Swap Used:1221016,00 GB13,80 GB<2,14 GB4,05 GB100% (Sun 12 Apr 22:26:18MemoryEnergyDiskNetworkPorts333106139100831381061251211101219895144163981582644014722153951465948558193109PID77300735779367855352306802727727613976938727337587275900389758587790975880756777592042420697756577559475556463176977896252972905App Memory:Wired Memory:Compressed:Usersunaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukasrootlukaslukaslukaslukasrootrootlukaslukaslukaslukas_analyticsdlukaslukaslukaslukas4,23 GB2,07 GB6,95 GB...
|
4128
|
|
68821
|
1564
|
2
|
2026-04-22T06:44:17.935020+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776840257935_m1.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Meet - Daily - Platform
Cl Meet - Daily - Platform
Meet - Daily - Platform
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Return to home screen
[EMAIL]
Switch account
Switch account
Lukas Kovalik
More options
Turn on microphone
Turn off camera
Turn on background blur
Microphone: MacBook Pro Microphone
Speaker: System Default Speaker Device
Camera: FaceTime HD Camera
Backgrounds and effects
Daily - Platform
Daily - Platform
in 1 minute
Use Gemini to take notes Share notes and transcript
Use Gemini to take notes
Share notes and transcript
Start...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.0,"top":0.072222225,"width":0.16631944,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Meet - Daily - Platform","depth":5,"bounds":{"left":0.027777778,"top":0.08777778,"width":0.08298611,"height":0.015},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.140625,"top":0.08222222,"width":0.016666668,"height":0.026666667},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.005902778,"top":0.12,"width":0.15486111,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.005902778,"top":0.9583333,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.028819444,"top":0.9583333,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.052083332,"top":0.9583333,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.07534722,"top":0.9583333,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.09861111,"top":0.9583333,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Return to home screen","depth":10,"bounds":{"left":0.17743056,"top":0.09,"width":0.072916664,"height":0.044444446},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.83923614,"top":0.09166667,"width":0.11631945,"height":0.018333333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Switch account","depth":11,"bounds":{"left":0.89375,"top":0.11,"width":0.061805554,"height":0.017777778},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Switch account","depth":12,"bounds":{"left":0.89375,"top":0.11,"width":0.061805554,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Lukas Kovalik","depth":14,"bounds":{"left":0.18854167,"top":0.29222223,"width":0.0625,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"More options","depth":13,"bounds":{"left":0.6263889,"top":0.27611113,"width":0.033333335,"height":0.053333335},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Turn on microphone","depth":14,"bounds":{"left":0.35277778,"top":0.6338889,"width":0.03888889,"height":0.053333335},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Turn off camera","depth":14,"bounds":{"left":0.4027778,"top":0.6338889,"width":0.03888889,"height":0.053333335},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Turn on background blur","depth":13,"bounds":{"left":0.45277777,"top":0.6338889,"width":0.03888889,"height":0.053333335},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Microphone: MacBook Pro Microphone","depth":13,"bounds":{"left":0.17743056,"top":0.7272222,"width":0.11805555,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Speaker: System Default Speaker Device","depth":13,"bounds":{"left":0.30104166,"top":0.7272222,"width":0.11840278,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Camera: FaceTime HD Camera","depth":13,"bounds":{"left":0.425,"top":0.7272222,"width":0.11805555,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Backgrounds and effects","depth":12,"bounds":{"left":0.5486111,"top":0.7272222,"width":0.11805555,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Daily - Platform","depth":11,"bounds":{"left":0.67777777,"top":0.33722222,"width":0.31111112,"height":0.04},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Daily - Platform","depth":14,"bounds":{"left":0.76666665,"top":0.33722222,"width":0.13333334,"height":0.04},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in 1 minute","depth":12,"bounds":{"left":0.79375,"top":0.38722223,"width":0.079166666,"height":0.033888888},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Use Gemini to take notes Share notes and transcript","depth":11,"bounds":{"left":0.72152776,"top":0.43944445,"width":0.22361112,"height":0.07111111},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Use Gemini to take notes","depth":12,"bounds":{"left":0.76319444,"top":0.4561111,"width":0.1125,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Share notes and transcript","depth":12,"bounds":{"left":0.76319444,"top":0.47777778,"width":0.1,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Start","depth":12,"bounds":{"left":0.88125,"top":0.45277777,"width":0.055555556,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
3482331728051927478
|
7892970486484688603
|
visual_change
|
accessibility
|
NULL
|
Meet - Daily - Platform
Meet - Daily - Platform
Cl Meet - Daily - Platform
Meet - Daily - Platform
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Return to home screen
[EMAIL]
Switch account
Switch account
Lukas Kovalik
More options
Turn on microphone
Turn off camera
Turn on background blur
Microphone: MacBook Pro Microphone
Speaker: System Default Speaker Device
Camera: FaceTime HD Camera
Backgrounds and effects
Daily - Platform
Daily - Platform
in 1 minute
Use Gemini to take notes Share notes and transcript
Use Gemini to take notes
Share notes and transcript
Start...
|
68819
|
|
39951
|
NULL
|
0
|
2026-04-16T14:22:53.526975+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776349373526_m2.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
UW PICO 5.09 New Buffer
[ Read 18 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 18 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 18 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.23320313,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.23554687,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.34394532,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.34628907,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.4546875,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.45703125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.56523436,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.56757814,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.67578125,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.678125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.7703125,"top":1.0,"width":0.021875,"height":-0.02013886},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.5078125,"top":1.0,"width":0.012890625,"height":-0.020833373},"role_description":"text"}]...
|
4739949273896043654
|
7891601026936532458
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
UW PICO 5.09 New Buffer
[ Read 18 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
39950
|
|
39953
|
NULL
|
0
|
2026-04-16T14:22:54.397348+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776349374397_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
UW PICO 5.09 New Buffer
[ Read 18 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 18 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 18 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.196875,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.20104167,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.39375,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.39791667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.5902778,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.59444445,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.78680557,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.79097223,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.48819444,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
4739949273896043654
|
7891601026936532458
|
visual_change
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
UW PICO 5.09 New Buffer
[ Read 18 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
NULL
|
|
40007
|
824
|
4
|
2026-04-16T15:14:20.633823+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776352460633_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
UW PICO 5.09 New Buffer
[ Read 18 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ alias sp-start
sp-start='npx screenpipe@latest record --disable-audio &'
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 18 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ alias sp-start\nsp-start='npx screenpipe@latest record --disable-audio &'\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 18 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ alias sp-start\nsp-start='npx screenpipe@latest record --disable-audio &'\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.196875,"top":0.05888889,"width":0.196875,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.20104167,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.39375,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.39791667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.5902778,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.59444445,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.78680557,"top":0.05888889,"width":0.19652778,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.79097223,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.9548611,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.48819444,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
-4913604326885148143
|
7890475127029657066
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
UW PICO 5.09 New Buffer
[ Read 18 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ alias sp-start
sp-start='npx screenpipe@latest record --disable-audio &'
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
40005
|
|
40008
|
825
|
5
|
2026-04-16T15:14:20.633886+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776352460633_m2.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
UW PICO 5.09 New Buffer
[ Read 18 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ alias sp-start
sp-start='npx screenpipe@latest record --disable-audio &'
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 18 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ alias sp-start\nsp-start='npx screenpipe@latest record --disable-audio &'\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","depth":4,"value":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');\"\n1722\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe\nlukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio\nlukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe\nlukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help\nincreased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)\nStart recording screen, audio, and serve the API\n\nUsage: screenpipe record [OPTIONS]\n\nOptions:\n -d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>\n Audio chunk duration in seconds\n \n [default: 30]\n\n -p, --port <PORT>\n Port to run the server on\n \n [default: 3030]\n\n --disable-audio\n Disable audio recording\n\n -i, --audio-device <AUDIO_DEVICE>\n Audio devices to use (can be specified multiple times)\n\n --use-system-default-audio\n Follow system default audio devices\n\n --data-dir <DATA_DIR>\n Data directory. Default to $HOME/.screenpipe\n\n --debug\n Enable debug logging for screenpipe modules\n\n -a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>\n Audio transcription engine to use\n\n Possible values:\n - deepgram\n - whisper-tiny\n - whisper-tiny-quantized\n - whisper-large\n - whisper-large-quantized\n - whisper-large-v3-turbo\n - whisper-large-v3-turbo-quantized\n - openai-compatible\n - qwen3-asr\n - parakeet\n - disabled: Disable transcription (audio capture only, no speech-to-text)\n \n [default: parakeet]\n\n -m, --monitor-id <MONITOR_ID>\n Monitor IDs to use\n\n --use-all-monitors\n Automatically record all monitors\n\n -l, --language <LANGUAGE>\n Languages for OCR/transcription\n \n [possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]\n\n --use-pii-removal\n Enable PII removal\n\n --filter-music\n Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)\n\n --disable-vision\n Disable vision recording\n\n --ignored-windows <IGNORED_WINDOWS>\n Windows to ignore (by title, uses contains matching)\n\n --included-windows <INCLUDED_WINDOWS>\n Windows to include (by title, uses contains matching)\n\n --ignored-urls <IGNORED_URLS>\n URLs to ignore for browser privacy filtering\n\n --deepgram-api-key <DEEPGRAM_API_KEY>\n Deepgram API Key for audio transcription\n\n --transcription-mode <TRANSCRIPTION_MODE>\n Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime\n\n Possible values:\n - realtime: Transcribe immediately as audio is captured\n - batch: Accumulate longer audio batches for better transcription quality (default)\n \n [default: batch]\n\n --disable-telemetry\n Disable telemetry\n\n --video-quality <VIDEO_QUALITY>\n Video quality preset: low, balanced, high, max\n \n [default: balanced]\n\n --enable-sync\n Enable cloud sync\n\n --sync-token <SYNC_TOKEN>\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 18 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \n API token for cloud sync\n \n [env: SCREENPIPE_SYNC_TOKEN=]\n\n --sync-password <SYNC_PASSWORD>\n Password for encrypting synced data\n \n [env: SCREENPIPE_SYNC_PASSWORD=]\n\n --sync-interval-secs <SYNC_INTERVAL_SECS>\n Interval between sync cycles in seconds\n \n [default: 300]\n\n --sync-machine-id <SYNC_MACHINE_ID>\n Override the machine ID for this device\n\n --pause-on-drm-content\n Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected\n\n --api-auth\n Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed\n\n --encrypt-secrets\n Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one\n\n --retention-days <RETENTION_DAYS>\n Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)\n \n [default: 14]\n\n -h, --help\n Print help (see a summary with '-h')\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps \"Boosteroid\"\nerror: unexpected argument '--ignored-apps' found\n\n tip: a similar argument exists: '--ignored-urls'\n\nUsage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>\n\nFor more information, try '--help'.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ alias sp-start\nsp-start='npx screenpipe@latest record --disable-audio &'\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.23320313,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.23554687,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.34394532,"top":1.0,"width":0.11074219,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.34628907,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.4546875,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.45703125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.56523436,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.56757814,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.67578125,"top":1.0,"width":0.11054687,"height":-0.03680551},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.678125,"top":1.0,"width":0.00625,"height":-0.039583325},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.7703125,"top":1.0,"width":0.021875,"height":-0.02013886},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.5078125,"top":1.0,"width":0.012890625,"height":-0.020833373},"role_description":"text"}]...
|
-4913604326885148143
|
7890475127029657066
|
click
|
accessibility
|
NULL
|
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screen lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT COUNT(*) FROM frames WHERE app_name = 'Boosteroid' AND timestamp >= datetime('now', '-1 hour');"
1722
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ps aux | grep screenpipe
lukas 10874 15.7 4.1 412962384 689376 s009 SN 3:49pm 32:48.19 /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --disable-audio
lukas 18357 0.9 0.0 410733264 1488 s010 S+ 4:53pm 0:00.00 grep screenpipe
lukas 10835 0.0 0.2 411427744 30240 s009 SN 3:49pm 0:00.08 node /Users/lukas/.nvm/versions/node/v14.15.4/bin/npx screenpipe@latest record --disable-audio
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ /Users/lukas/.npm/_npx/10835/lib/node_modules/screenpipe/node_modules/@screenpipe/cli-darwin-arm64/bin/screenpipe record --help
increased file descriptor limit from 256 to 8192 (hard limit: 9223372036854775807)
Start recording screen, audio, and serve the API
Usage: screenpipe record [OPTIONS]
Options:
-d, --audio-chunk-duration <AUDIO_CHUNK_DURATION>
Audio chunk duration in seconds
[default: 30]
-p, --port <PORT>
Port to run the server on
[default: 3030]
--disable-audio
Disable audio recording
-i, --audio-device <AUDIO_DEVICE>
Audio devices to use (can be specified multiple times)
--use-system-default-audio
Follow system default audio devices
--data-dir <DATA_DIR>
Data directory. Default to $HOME/.screenpipe
--debug
Enable debug logging for screenpipe modules
-a, --audio-transcription-engine <AUDIO_TRANSCRIPTION_ENGINE>
Audio transcription engine to use
Possible values:
- deepgram
- whisper-tiny
- whisper-tiny-quantized
- whisper-large
- whisper-large-quantized
- whisper-large-v3-turbo
- whisper-large-v3-turbo-quantized
- openai-compatible
- qwen3-asr
- parakeet
- disabled: Disable transcription (audio capture only, no speech-to-text)
[default: parakeet]
-m, --monitor-id <MONITOR_ID>
Monitor IDs to use
--use-all-monitors
Automatically record all monitors
-l, --language <LANGUAGE>
Languages for OCR/transcription
[possible values: english, chinese, german, spanish, russian, korean, french, japanese, portuguese, turkish, polish, catalan, dutch, arabic, swedish, italian, indonesian, hindi, vietnamese, finnish, hebrew, ukrainian, greek, malay, czech, romanian, danish, hungarian, norwegian, thai, urdu, croatian, bulgarian, lithuanian, latin, malayalam, welsh, slovak, persian, latvian, bengali, serbian, azerbaijani, slovenian, estonian, macedonian, nepali, mongolian, bosnian, kazakh, albanian, swahili, galician, marathi, punjabi, sinhala, khmer, afrikaans, belarusian, gujarati, amharic, yiddish, lao, uzbek, faroese, pashto, maltese, sanskrit, luxembourgish, myanmar, tibetan, tagalog, assamese, tatar, hausa, javanese]
--use-pii-removal
Enable PII removal
--filter-music
Filter music-dominant audio before transcription (reduces Spotify/YouTube music noise)
--disable-vision
Disable vision recording
--ignored-windows <IGNORED_WINDOWS>
Windows to ignore (by title, uses contains matching)
--included-windows <INCLUDED_WINDOWS>
Windows to include (by title, uses contains matching)
--ignored-urls <IGNORED_URLS>
URLs to ignore for browser privacy filtering
--deepgram-api-key <DEEPGRAM_API_KEY>
Deepgram API Key for audio transcription
--transcription-mode <TRANSCRIPTION_MODE>
Audio transcription scheduling mode: batch (default, longer chunks for quality) or realtime
Possible values:
- realtime: Transcribe immediately as audio is captured
- batch: Accumulate longer audio batches for better transcription quality (default)
[default: batch]
--disable-telemetry
Disable telemetry
--video-quality <VIDEO_QUALITY>
Video quality preset: low, balanced, high, max
[default: balanced]
--enable-sync
Enable cloud sync
--sync-token <SYNC_TOKEN>
UW PICO 5.09 New Buffer
[ Read 18 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
API token for cloud sync
[env: SCREENPIPE_SYNC_TOKEN=]
--sync-password <SYNC_PASSWORD>
Password for encrypting synced data
[env: SCREENPIPE_SYNC_PASSWORD=[PASSWORD]
--sync-interval-secs <SYNC_INTERVAL_SECS>
Interval between sync cycles in seconds
[default: 300]
--sync-machine-id <SYNC_MACHINE_ID>
Override the machine ID for this device
--pause-on-drm-content
Pause screen and audio capture when DRM content (Netflix, Disney+, etc.) is detected
--api-auth
Require authentication for remote API access. When enabled, non-localhost requests must include Authorization: Bearer <SCREENPIPE_API_KEY>. Localhost requests are always allowed
--encrypt-secrets
Encrypt secrets (API keys, OAuth tokens) at rest using the OS keychain. Creates a keychain key if one doesn't exist. Without this flag, the CLI will use an existing key (created by the desktop app) but won't create one
--retention-days <RETENTION_DAYS>
Local data retention in days. Old screen/audio data is auto-deleted after this period. Set to 0 to disable retention (keep data forever)
[default: 14]
-h, --help
Print help (see a summary with '-h')
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ npx screenpipe@latest record --disable-audio --ignored-apps "Boosteroid"
error: unexpected argument '--ignored-apps' found
tip: a similar argument exists: '--ignored-urls'
Usage: screenpipe record --disable-audio --ignored-urls <IGNORED_URLS>
For more information, try '--help'.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano ~/.screenpipe/config.json
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ alias sp-start
sp-start='npx screenpipe@latest record --disable-audio &'
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
screenpipe"
Close Tab
-zsh
Close Tab
⌥⌘1
-zsh...
|
40006
|