File: //proc/thread-self/root/tmp/creator_direct_audit_cli.patch
*** Begin Patch
*** Add File: /home/kiwerkzeuge.de/private/creator-studio/bin/creator_studio_direct_publish_audit_pack.php
+#!/usr/bin/env php
+<?php
+declare(strict_types=1);
+
+require '/home/kiwerkzeuge.de/public_html/creator-studio/_lib/bootstrap.php';
+
+cs_bootstrap();
+
+$options = getopt('', [
+ 'json',
+ 'write',
+ 'record-event',
+ 'source::',
+ 'is-test',
+]);
+if ($options === false) {
+ $options = [];
+}
+
+function direct_audit_cli_option(array $options, string $name, string $default = ''): string
+{
+ if (!array_key_exists($name, $options)) {
+ return $default;
+ }
+ $value = $options[$name];
+ if (is_array($value)) {
+ $value = end($value);
+ }
+ return ($value === false || $value === null) ? $default : (string) $value;
+}
+
+function direct_audit_collect_metrics(): array
+{
+ $accountStore = cs_load_account_store();
+ $accounts = array_values(array_filter($accountStore['accounts'] ?? [], 'is_array'));
+ $connectedAccounts = 0;
+ $creatorInfoReady = 0;
+ $publishOps = 0;
+ $draftUploads = 0;
+ $directPublishes = 0;
+ $pullFromUrlPublishes = 0;
+ $photoPublishes = 0;
+ $publishFailures = 0;
+
+ foreach ($accounts as $account) {
+ $openId = trim((string) ($account['open_id'] ?? ''));
+ $hasToken = trim((string) ($account['access_token'] ?? '')) !== '' || trim((string) ($account['refresh_token'] ?? '')) !== '';
+ if ($openId !== '' || $hasToken) {
+ $connectedAccounts += 1;
+ }
+ if (is_array($account['creator_info'] ?? null) && ($account['creator_info'] ?? []) !== []) {
+ $creatorInfoReady += 1;
+ }
+ foreach (array_values(array_filter($account['operations'] ?? [], 'is_array')) as $operation) {
+ $publishOps += 1;
+ $mode = strtolower(trim((string) ($operation['mode'] ?? '')));
+ $status = strtoupper(trim((string) ($operation['status'] ?? '')));
+ $source = strtoupper(trim((string) ($operation['source'] ?? '')));
+ $mediaType = strtoupper(trim((string) ($operation['media_type'] ?? '')));
+ $isFailure = $status !== '' && (str_contains($status, 'FAIL') || str_contains($status, 'ERROR') || str_contains($status, 'REJECT'));
+ if (!$isFailure && in_array($mode, ['draft', 'upload', 'upload_draft', 'photo_upload'], true)) {
+ $draftUploads += 1;
+ }
+ if (!$isFailure && in_array($mode, ['direct', 'post-video', 'publish', 'photo_direct'], true)) {
+ $directPublishes += 1;
+ }
+ if (!$isFailure && ($source === 'PULL_FROM_URL' || isset($operation['source_url']) || isset($operation['source_urls']))) {
+ $pullFromUrlPublishes += 1;
+ }
+ if (!$isFailure && ($mediaType === 'PHOTO' || str_contains($mode, 'photo'))) {
+ $photoPublishes += 1;
+ }
+ if ($isFailure) {
+ $publishFailures += 1;
+ }
+ }
+ }
+
+ $publishLedger = cs_publish_ledger_summary($accounts);
+ $ledgerSummary = is_array($publishLedger['summary'] ?? null) ? $publishLedger['summary'] : cs_publish_ledger_empty_summary();
+ $receiptSummary = cs_creator_publish_receipt_summary(cs_creator_publish_receipt_list(true));
+
+ $publishOps = max($publishOps, (int) ($ledgerSummary['publish_ops'] ?? 0));
+ $draftUploads = max($draftUploads, (int) ($ledgerSummary['draft_uploads'] ?? 0));
+ $directPublishes = max($directPublishes, (int) ($ledgerSummary['direct_publishes'] ?? 0));
+ $pullFromUrlPublishes = max($pullFromUrlPublishes, (int) ($ledgerSummary['pull_from_url_publishes'] ?? 0));
+ $photoPublishes = max($photoPublishes, (int) ($ledgerSummary['photo_publishes'] ?? 0));
+ $publishFailures = max($publishFailures, (int) ($ledgerSummary['publish_failures'] ?? 0));
+
+ $complianceControlsVisible = $creatorInfoReady > 0;
+ return [
+ 'connected_accounts' => $connectedAccounts,
+ 'creator_info_ready_accounts' => $creatorInfoReady,
+ 'publish_ops' => $publishOps,
+ 'draft_uploads' => $draftUploads,
+ 'direct_publishes' => $directPublishes,
+ 'self_only_direct_publishes' => $directPublishes,
+ 'pull_from_url_publishes' => $pullFromUrlPublishes,
+ 'photo_publishes' => $photoPublishes,
+ 'publish_failures' => $publishFailures,
+ 'creator_publish_receipts' => (int) ($receiptSummary['real_total'] ?? 0),
+ 'creator_publish_receipt_tests' => (int) ($receiptSummary['test_total'] ?? 0),
+ 'creator_publish_receipts_checklist_passed' => (int) ($receiptSummary['checklist_passed'] ?? 0),
+ 'creator_publish_receipts_posted_publicly' => (int) ($receiptSummary['posted_publicly'] ?? 0),
+ 'compliance_controls_visible' => $complianceControlsVisible,
+ 'ai_disclosure_supported' => $complianceControlsVisible,
+ ];
+}
+
+function direct_audit_evidence(array $metrics): array
+{
+ return [
+ 'tiktok_app_configured' => [
+ 'ready' => cs_config_is_ready(),
+ 'label' => 'TikTok app client, redirect, and scopes configured',
+ ],
+ 'creator_info_ready' => [
+ 'ready' => (int) ($metrics['creator_info_ready_accounts'] ?? 0) > 0,
+ 'label' => 'Creator Info available for at least one connected account',
+ ],
+ 'draft_upload_proven' => [
+ 'ready' => (int) ($metrics['draft_uploads'] ?? 0) > 0,
+ 'label' => 'Draft upload path has a successful operation',
+ ],
+ 'self_only_direct_publish_proven' => [
+ 'ready' => (int) ($metrics['self_only_direct_publishes'] ?? 0) > 0,
+ 'label' => 'SELF_ONLY Direct Publish path has a successful operation',
+ ],
+ 'pull_from_url_delivery_proven' => [
+ 'ready' => (int) ($metrics['pull_from_url_publishes'] ?? 0) > 0,
+ 'label' => 'Hosted PULL_FROM_URL delivery has a successful operation',
+ ],
+ 'photo_carousel_delivery_proven' => [
+ 'ready' => (int) ($metrics['photo_publishes'] ?? 0) > 0,
+ 'label' => 'Photo/Carousel delivery has a successful operation',
+ ],
+ 'compliance_controls_visible' => [
+ 'ready' => !empty($metrics['compliance_controls_visible']),
+ 'label' => 'Privacy, interaction, commercial content, and AIGC controls are rendered from Creator Info',
+ ],
+ 'creator_receipt_collected' => [
+ 'ready' => (int) ($metrics['creator_publish_receipts'] ?? 0) > 0,
+ 'label' => 'Real creator receipt confirms TikTok editor completion and compliance choices',
+ ],
+ 'public_business_direct_approval' => [
+ 'ready' => false,
+ 'label' => 'TikTok Advanced Access for public/business Direct Post is approved',
+ ],
+ ];
+}
+
+function direct_audit_markdown(array $pack): string
+{
+ $lines = [
+ '# Direct Publish Audit Evidence Pack',
+ '',
+ 'Generated at: ' . (string) $pack['generated_at'],
+ 'Status: ' . (string) $pack['status'],
+ 'Metric: ' . (string) $pack['metric'],
+ '',
+ '## Evidence Checklist',
+ ];
+ foreach ($pack['evidence'] as $key => $item) {
+ $lines[] = '- [' . (!empty($item['ready']) ? 'x' : ' ') . '] ' . (string) $item['label'] . ' (`' . (string) $key . '`)';
+ }
+ $lines[] = '';
+ $lines[] = '## Safe Metrics';
+ foreach ($pack['metrics'] as $key => $value) {
+ if (is_bool($value)) {
+ $value = $value ? 'true' : 'false';
+ }
+ $lines[] = '- ' . (string) $key . ': ' . (string) $value;
+ }
+ $lines[] = '';
+ $lines[] = '## Public Direct Publish Blocker';
+ $lines[] = (string) $pack['public_direct_blocker'];
+ $lines[] = '';
+ $lines[] = '## Next Action';
+ $lines[] = (string) $pack['next_action'];
+ $lines[] = '';
+ $lines[] = '## Privacy Boundary';
+ $lines[] = 'This pack intentionally excludes TikTok open IDs, access tokens, refresh tokens, publish IDs, signed URLs, checkout references, and private media paths.';
+ $lines[] = '';
+ return implode("\n", $lines);
+}
+
+try {
+ $source = direct_audit_cli_option($options, 'source', 'direct_publish_audit_pack_cli');
+ $isTest = array_key_exists('is-test', $options);
+ $write = array_key_exists('write', $options) || array_key_exists('record-event', $options);
+ $metrics = direct_audit_collect_metrics();
+ $evidence = direct_audit_evidence($metrics);
+ $readyEvidence = count(array_filter($evidence, static fn (array $item): bool => !empty($item['ready'])));
+ $totalEvidence = count($evidence);
+ $hardBlocked = empty($evidence['tiktok_app_configured']['ready']) || empty($evidence['creator_info_ready']['ready']);
+ $publicDirectBlocked = empty($evidence['public_business_direct_approval']['ready']);
+ $blockedEvidence = $publicDirectBlocked ? 1 : 0;
+ $status = $hardBlocked ? 'blocked' : ($readyEvidence === $totalEvidence ? 'ready' : 'action_needed');
+ $metric = $readyEvidence . '/' . $totalEvidence . ' evidence ยท ' . ($publicDirectBlocked ? 'public direct pending' : 'public direct ready');
+ $now = cs_now_iso();
+ $pack = [
+ 'pack_id' => 'dpaud_' . gmdate('YmdHis') . '_' . substr(hash('sha256', $now . json_encode($metrics, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)), 0, 8),
+ 'generated_at' => $now,
+ 'updated_at' => $now,
+ 'status' => $status,
+ 'metric' => $metric,
+ 'next_action' => $publicDirectBlocked
+ ? 'Submit the sanitized evidence pack in TikTok Developer Portal and request Advanced Access for public/business Direct Post.'
+ : 'Keep audit evidence fresh and monitor public Direct Post runs.',
+ 'source' => $source,
+ 'is_test' => $isTest,
+ 'ready_evidence' => $readyEvidence,
+ 'total_evidence' => $totalEvidence,
+ 'blocked_evidence' => $blockedEvidence,
+ 'evidence_status' => $status,
+ 'public_direct_blocker' => $publicDirectBlocked ? 'tiktok_advanced_access_for_public_business_direct_post_unresolved' : '',
+ 'advanced_access_next_step' => 'Submit sanitized app evidence, screenshots of compliance controls, and operation counts without raw IDs or tokens.',
+ 'private_pack_present' => false,
+ 'private_pack_hash' => '',
+ 'metrics' => $metrics,
+ 'evidence' => $evidence,
+ ];
+
+ $markdown = direct_audit_markdown($pack);
+ if ($write) {
+ if (!is_dir(CREATOR_STUDIO_DIRECT_PUBLISH_AUDIT_DIR)) {
+ cs_mkdir_owned(CREATOR_STUDIO_DIRECT_PUBLISH_AUDIT_DIR, 0700, true);
+ }
+ $packHash = hash('sha256', $markdown);
+ $path = CREATOR_STUDIO_DIRECT_PUBLISH_AUDIT_DIR . '/' . $pack['pack_id'] . '.md';
+ $tempPath = $path . '.tmp';
+ file_put_contents($tempPath, $markdown, LOCK_EX);
+ chmod($tempPath, 0600);
+ rename($tempPath, $path);
+ chmod($path, 0600);
+ $pack['private_pack_present'] = true;
+ $pack['private_pack_hash'] = $packHash;
+ $saved = cs_direct_publish_audit_pack_save($pack);
+ $summary = cs_direct_publish_audit_pack_summary([$saved]);
+ if (array_key_exists('record-event', $options)) {
+ $summary['event'] = cs_direct_publish_audit_pack_record_event($summary, [
+ 'source' => $source,
+ 'is_test' => $isTest,
+ ]);
+ }
+ } else {
+ $summary = cs_direct_publish_audit_pack_summary([$pack]);
+ }
+} catch (Throwable $exception) {
+ if (array_key_exists('json', $options)) {
+ echo json_encode([
+ 'generated_at' => cs_now_iso(),
+ 'ok' => false,
+ 'error' => $exception->getMessage(),
+ ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), PHP_EOL;
+ exit(1);
+ }
+ fwrite(STDERR, 'ERROR: ' . $exception->getMessage() . PHP_EOL);
+ exit(1);
+}
+
+$payload = [
+ 'generated_at' => cs_now_iso(),
+ 'ok' => true,
+ 'summary' => cs_direct_publish_audit_pack_agent_payload($summary),
+ 'checklist' => $evidence,
+ 'event' => $summary['event'] ?? null,
+];
+
+if (array_key_exists('json', $options)) {
+ echo json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), PHP_EOL;
+ exit(0);
+}
+
+echo 'Creator Studio Direct Publish audit pack', PHP_EOL;
+echo 'generated_at: ', $payload['generated_at'], PHP_EOL;
+echo 'status: ', $payload['summary']['status'], PHP_EOL;
+echo 'metric: ', $payload['summary']['metric'], PHP_EOL;
+echo 'next: ', $payload['summary']['next_action'], PHP_EOL;
+if (is_array($payload['event'] ?? null)) {
+ echo 'event_recorded: ', !empty($payload['event']['recorded']) ? 'yes' : 'no', PHP_EOL;
+}
*** End Patch