@playdrop/playdrop-cli 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,3 @@
1
- export declare function validate(pathOrName: string): Promise<void>;
1
+ export declare function validate(pathOrName: string, options?: {
2
+ env?: string;
3
+ }): Promise<void>;
@@ -57,8 +57,11 @@ function validateAssetTask(task, assetSpecLookups) {
57
57
  throw new Error(errors.join(' '));
58
58
  }
59
59
  }
60
- async function loadAuthenticatedValidationContext(workspacePath) {
61
- const ctx = await (0, commandContext_1.resolveOptionalEnvironmentContext)('project validate', { workspacePath });
60
+ async function loadAuthenticatedValidationContext(workspacePath, envOverride) {
61
+ const ctx = await (0, commandContext_1.resolveOptionalEnvironmentContext)('project validate', {
62
+ env: envOverride,
63
+ workspacePath,
64
+ });
62
65
  if (!ctx) {
63
66
  return null;
64
67
  }
@@ -79,8 +82,11 @@ async function loadAuthenticatedValidationContext(workspacePath) {
79
82
  return null;
80
83
  }
81
84
  }
82
- async function loadHostedLaunchValidationContext(workspacePath) {
83
- const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project validate', 'run hosted app launch validation', { workspacePath });
85
+ async function loadHostedLaunchValidationContext(workspacePath, envOverride) {
86
+ const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('project validate', 'run hosted app launch validation', {
87
+ env: envOverride,
88
+ workspacePath,
89
+ });
84
90
  if (!ctx) {
85
91
  return null;
86
92
  }
@@ -110,6 +116,12 @@ async function loadHostedLaunchValidationContext(workspacePath) {
110
116
  token: ctx.token,
111
117
  };
112
118
  }
119
+ function printResolvedValidationAccount(ctx) {
120
+ const username = typeof ctx.account?.username === 'string' && ctx.account.username.trim().length > 0
121
+ ? ctx.account.username.trim()
122
+ : 'unknown';
123
+ console.log(`Resolved account: ${username} (${ctx.env})`);
124
+ }
113
125
  function resolveValidationWorkspacePath(pathOrName, resolution) {
114
126
  return resolution === 'name' ? process.cwd() : pathOrName;
115
127
  }
@@ -118,8 +130,8 @@ function formatLiveTagClearWarning(input) {
118
130
  return `${input.entityLabel} "${input.displayName}" would remove ${countLabel} on publish. Add a tags field or pass --clear-tags to confirm.`;
119
131
  }
120
132
  // eslint-disable-next-line complexity
121
- async function collectLiveTagClearWarnings(tasks, workspacePath) {
122
- const context = await loadAuthenticatedValidationContext(workspacePath);
133
+ async function collectLiveTagClearWarnings(tasks, workspacePath, envOverride) {
134
+ const context = await loadAuthenticatedValidationContext(workspacePath, envOverride);
123
135
  if (!context) {
124
136
  return [];
125
137
  }
@@ -176,9 +188,16 @@ async function collectLiveTagClearWarnings(tasks, workspacePath) {
176
188
  }
177
189
  return warnings;
178
190
  }
179
- async function validate(pathOrName) {
191
+ async function validate(pathOrName, options = {}) {
180
192
  const selection = (0, taskSelection_1.selectTasks)(pathOrName);
181
193
  const workspacePath = resolveValidationWorkspacePath(pathOrName, selection.resolution);
194
+ const resolvedValidationContext = await (0, commandContext_1.resolveOptionalEnvironmentContext)('project validate', {
195
+ env: options.env,
196
+ workspacePath,
197
+ });
198
+ if (resolvedValidationContext) {
199
+ printResolvedValidationAccount(resolvedValidationContext);
200
+ }
182
201
  if (selection.errors.length > 0) {
183
202
  (0, taskSelection_1.reportTaskErrors)(selection.errors, 'project validate');
184
203
  process.exitCode = 1;
@@ -206,7 +225,7 @@ async function validate(pathOrName) {
206
225
  const sortedTasks = (0, taskUtils_1.sortTasks)(tasks);
207
226
  const hostedLaunchValidationRequired = sortedTasks.some((task) => task.kind === 'app' && task.hostingMode !== 'EXTERNAL' && !task.externalUrl);
208
227
  const hostedLaunchValidationContext = hostedLaunchValidationRequired
209
- ? await loadHostedLaunchValidationContext(workspacePath)
228
+ ? await loadHostedLaunchValidationContext(workspacePath, options.env)
210
229
  : null;
211
230
  if (hostedLaunchValidationRequired && !hostedLaunchValidationContext) {
212
231
  return;
@@ -273,7 +292,7 @@ async function validate(pathOrName) {
273
292
  console.log((0, uploadLog_1.formatTaskLogLine)(entry));
274
293
  }
275
294
  }
276
- const liveTagWarnings = await collectLiveTagClearWarnings(sortedTasks, workspacePath);
295
+ const liveTagWarnings = await collectLiveTagClearWarnings(sortedTasks, workspacePath, options.env);
277
296
  liveTagWarnings.forEach((warning) => warnings.add(warning));
278
297
  (0, uploadLog_1.printTaskSummary)(results, warnings, { action: 'validate' });
279
298
  }
@@ -1 +1,3 @@
1
- export declare function whoami(): Promise<void>;
1
+ export declare function whoami(options?: {
2
+ env?: string;
3
+ }): Promise<void>;
@@ -6,8 +6,11 @@ const config_1 = require("../config");
6
6
  const commandContext_1 = require("../commandContext");
7
7
  const http_1 = require("../http");
8
8
  const messages_1 = require("../messages");
9
- async function whoami() {
10
- const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('whoami', 'Checking your Playdrop account status', { workspacePath: process.cwd() });
9
+ async function whoami(options = {}) {
10
+ const ctx = await (0, commandContext_1.resolveAuthenticatedEnvironmentContext)('auth whoami', 'Checking your Playdrop account status', {
11
+ env: options.env,
12
+ workspacePath: process.cwd(),
13
+ });
11
14
  if (!ctx) {
12
15
  return;
13
16
  }
@@ -65,7 +68,11 @@ async function whoami() {
65
68
  }
66
69
  if (resolvedCtx.workspaceAuth) {
67
70
  console.log(`Workspace account: ${username} (${resolvedCtx.env})`);
68
- console.log('Resolved from: .playdrop.json');
71
+ const resolutionSources = ['.playdrop.json'];
72
+ if (options.env) {
73
+ resolutionSources.push(`--env ${options.env}`);
74
+ }
75
+ console.log(`Resolved from: ${resolutionSources.join(' + ')}`);
69
76
  if (globalCurrentAccount
70
77
  && (globalCurrentAccount.username !== username || globalCurrentAccount.env !== resolvedCtx.env)) {
71
78
  console.log(`Global default: ${globalCurrentAccount.username} (${globalCurrentAccount.env})`);
@@ -73,6 +80,9 @@ async function whoami() {
73
80
  }
74
81
  else {
75
82
  console.log(`${username} (${resolvedCtx.env})`);
83
+ if (options.env) {
84
+ console.log(`Resolved from: --env ${options.env}`);
85
+ }
76
86
  }
77
87
  console.log('Next: run "playdrop getting-started" to see the recommended workflow.');
78
88
  }
package/dist/index.js CHANGED
@@ -135,8 +135,9 @@ function registerWhoamiCommand(parent) {
135
135
  parent
136
136
  .command('whoami')
137
137
  .description('Show the resolved account for this workspace')
138
- .action(async () => {
139
- await (0, whoami_1.whoami)();
138
+ .option('--env <env>', 'Resolve this command against one logged-in environment')
139
+ .action(async (opts) => {
140
+ await (0, whoami_1.whoami)({ env: opts.env });
140
141
  });
141
142
  }
142
143
  function registerAccountsCommand(parent) {
@@ -799,8 +800,9 @@ projectCapture
799
800
  project
800
801
  .command('validate [target]')
801
802
  .description('Validate catalogue content')
802
- .action(async (target) => {
803
- await (0, validate_1.validate)(target ?? '.');
803
+ .option('--env <env>', 'Resolve this command against one logged-in environment')
804
+ .action(async (target, opts) => {
805
+ await (0, validate_1.validate)(target ?? '.', { env: opts.env });
804
806
  });
805
807
  project
806
808
  .command('build [target]')
@@ -817,11 +819,17 @@ project
817
819
  project
818
820
  .command('publish [target]')
819
821
  .description('Publish local content to Playdrop')
822
+ .option('--env <env>', 'Resolve this command against one logged-in environment')
820
823
  .option('--skip-ecs', 'Skip ecs.json and server.js during publish')
821
824
  .option('--clear-tags', 'Confirm clearing existing live tags when this publish removes them')
822
825
  .addOption(new commander_1.Option('--skip-review', 'Mark uploaded app versions as NOT_REQUIRED review. Admin-only.').hideHelp())
823
826
  .action(async (target, opts) => {
824
- await (0, upload_1.upload)(target ?? '.', { skipEcs: opts.skipEcs, skipReview: opts.skipReview, clearTags: opts.clearTags });
827
+ await (0, upload_1.upload)(target ?? '.', {
828
+ env: opts.env,
829
+ skipEcs: opts.skipEcs,
830
+ skipReview: opts.skipReview,
831
+ clearTags: opts.clearTags,
832
+ });
825
833
  });
826
834
  const documentation = program.command('documentation').description('Public Playdrop documentation');
827
835
  documentation
@@ -14,6 +14,9 @@ type PlaywrightModule = {
14
14
  }): Promise<BrowserContext>;
15
15
  };
16
16
  };
17
+ type PlaydropBrowserContextOptions = BrowserContextOptions & {
18
+ automationOrigin?: string | null;
19
+ };
17
20
  export declare function setPlaywrightLoader(loader: (() => Promise<PlaywrightModule>) | null): void;
18
21
  export declare function createPlaywrightLaunchError(tool: string, error: unknown): Error;
19
22
  type PageCallback<T> = (context: {
@@ -21,6 +24,6 @@ type PageCallback<T> = (context: {
21
24
  context: BrowserContext;
22
25
  page: Page;
23
26
  }) => Promise<T>;
24
- export declare function withChromiumPage<T>(callback: PageCallback<T>, contextOptions?: BrowserContextOptions): Promise<T>;
25
- export declare function launchPersistentChromiumContext(userDataDir: string, options?: BrowserContextOptions): Promise<BrowserContext>;
27
+ export declare function withChromiumPage<T>(callback: PageCallback<T>, options?: PlaydropBrowserContextOptions): Promise<T>;
28
+ export declare function launchPersistentChromiumContext(userDataDir: string, options?: PlaydropBrowserContextOptions): Promise<BrowserContext>;
26
29
  export {};
@@ -91,7 +91,23 @@ function createPlaywrightLaunchError(tool, error) {
91
91
  }
92
92
  return error instanceof Error ? error : new Error(message);
93
93
  }
94
- async function withChromiumPage(callback, contextOptions = {}) {
94
+ async function grantAutomationLocalNetworkAccess(context, origin) {
95
+ if (!origin?.trim()) {
96
+ return;
97
+ }
98
+ const grantPermissions = context.grantPermissions;
99
+ if (typeof grantPermissions !== 'function') {
100
+ return;
101
+ }
102
+ try {
103
+ await grantPermissions.call(context, ['local-network-access'], { origin });
104
+ }
105
+ catch (error) {
106
+ const message = error instanceof Error ? error.message : String(error);
107
+ console.warn(`[${TOOL_NAME}] Failed to grant Playwright local-network-access for ${origin}: ${message}`);
108
+ }
109
+ }
110
+ async function withChromiumPage(callback, options = {}) {
95
111
  const { chromium } = await loadPlaywright();
96
112
  let browser;
97
113
  try {
@@ -102,7 +118,9 @@ async function withChromiumPage(callback, contextOptions = {}) {
102
118
  }
103
119
  let context = null;
104
120
  try {
121
+ const { automationOrigin, ...contextOptions } = options;
105
122
  context = await browser.newContext(contextOptions);
123
+ await grantAutomationLocalNetworkAccess(context, automationOrigin);
106
124
  const page = await context.newPage();
107
125
  return await callback({ browser, context, page });
108
126
  }
@@ -131,12 +149,15 @@ async function launchPersistentChromiumContext(userDataDir, options = {}) {
131
149
  throw new Error(`[${TOOL_NAME}] Your Playwright Chromium build does not support persistent contexts.`);
132
150
  }
133
151
  try {
134
- return await chromium.launchPersistentContext(userDataDir, {
135
- ...options,
152
+ const { automationOrigin, ...contextOptions } = options;
153
+ const context = await chromium.launchPersistentContext(userDataDir, {
154
+ ...contextOptions,
136
155
  headless: false,
137
156
  args: [...exports.CHROMIUM_ARGS],
138
157
  chromiumSandbox: false,
139
158
  });
159
+ await grantAutomationLocalNetworkAccess(context, automationOrigin);
160
+ return context;
140
161
  }
141
162
  catch (error) {
142
163
  throw createPlaywrightLaunchError(TOOL_NAME, error);
@@ -57,7 +57,7 @@ function selectTasks(pathOrName) {
57
57
  });
58
58
  return { tasks: [], warnings, errors, resolution: 'directory' };
59
59
  }
60
- const { apps, assetSpecs, assets, ownedAssets, assetPacks, warnings: rawWarnings, errors: catalogueErrors } = (0, catalogue_1.resolveCatalogueEntries)(absolute);
60
+ const { apps, assetSpecs, assets, assetPacks, warnings: rawWarnings, errors: catalogueErrors } = (0, catalogue_1.resolveCatalogueEntries)(absolute);
61
61
  if (catalogueErrors.length > 0) {
62
62
  catalogueErrors.forEach(message => {
63
63
  errors.push({
@@ -68,7 +68,7 @@ function selectTasks(pathOrName) {
68
68
  });
69
69
  return { tasks: [], warnings, errors, resolution: 'directory' };
70
70
  }
71
- const taskList = [...assetSpecs, ...assets, ...apps, ...ownedAssets, ...assetPacks];
71
+ const taskList = [...assetSpecs, ...assets, ...apps, ...assetPacks];
72
72
  if (taskList.length === 0) {
73
73
  errors.push({
74
74
  type: 'no-tasks',
@@ -132,7 +132,6 @@ function selectTasks(pathOrName) {
132
132
  ...lookup.apps.filter(task => task.name === pathOrName),
133
133
  ...lookup.assetSpecs.filter(task => task.name === pathOrName),
134
134
  ...lookup.assets.filter(task => task.name === pathOrName),
135
- ...lookup.ownedAssets.filter(task => task.name === pathOrName),
136
135
  ...lookup.assetPacks.filter(task => task.name === pathOrName),
137
136
  ];
138
137
  if (candidates.length === 0) {
package/dist/taskUtils.js CHANGED
@@ -5,11 +5,11 @@ exports.addTaskWarnings = addTaskWarnings;
5
5
  const catalogue_1 = require("./catalogue");
6
6
  function sortTasks(tasks) {
7
7
  const order = {
8
- 'asset-spec': -1,
9
- asset: 0,
10
- app: 1,
11
- 'owned-asset': 2,
8
+ 'asset-spec': 0,
9
+ asset: 1,
10
+ app: 2,
12
11
  'asset-pack': 3,
12
+ 'owned-asset': 4,
13
13
  };
14
14
  return [...tasks].sort((a, b) => {
15
15
  if (order[a.kind] !== order[b.kind]) {
@@ -1,5 +1,5 @@
1
1
  export type TaskAction = 'validate' | 'build' | 'format' | 'upload';
2
- export type TaskStatus = 'success' | 'skipped' | 'error';
2
+ export type TaskStatus = 'success' | 'skipped' | 'blocked' | 'error';
3
3
  export type TaskLogEntry = {
4
4
  action: TaskAction;
5
5
  status: TaskStatus;
package/dist/uploadLog.js CHANGED
@@ -6,27 +6,32 @@ exports.printPublishedAppNextSteps = printPublishedAppNextSteps;
6
6
  const STATUS_EMOJI = {
7
7
  success: '✅',
8
8
  skipped: '➖',
9
+ blocked: '⛔',
9
10
  error: '❌',
10
11
  };
11
12
  const ACTION_VERBS = {
12
13
  validate: {
13
14
  success: 'validated',
14
15
  skipped: 'skipped',
16
+ blocked: 'blocked',
15
17
  error: '',
16
18
  },
17
19
  build: {
18
20
  success: 'built',
19
21
  skipped: 'skipped',
22
+ blocked: 'blocked',
20
23
  error: '',
21
24
  },
22
25
  format: {
23
26
  success: 'formatted',
24
27
  skipped: 'skipped',
28
+ blocked: 'blocked',
25
29
  error: '',
26
30
  },
27
31
  upload: {
28
32
  success: '',
29
33
  skipped: '',
34
+ blocked: '',
30
35
  error: '',
31
36
  },
32
37
  };
@@ -46,16 +51,19 @@ function formatTaskLogLine(entry) {
46
51
  function summarize(entries) {
47
52
  let success = 0;
48
53
  let skipped = 0;
54
+ let blocked = 0;
49
55
  let failed = 0;
50
56
  for (const entry of entries) {
51
57
  if (entry.status === 'success')
52
58
  success += 1;
53
59
  if (entry.status === 'skipped')
54
60
  skipped += 1;
61
+ if (entry.status === 'blocked')
62
+ blocked += 1;
55
63
  if (entry.status === 'error')
56
64
  failed += 1;
57
65
  }
58
- return { success, skipped, failed };
66
+ return { success, skipped, blocked, failed };
59
67
  }
60
68
  function printTaskSummary(entries, warnings, options) {
61
69
  const warningList = Array.from(warnings);
@@ -64,7 +72,7 @@ function printTaskSummary(entries, warnings, options) {
64
72
  console.log(`⚠️ ${warning}`);
65
73
  }
66
74
  }
67
- const { success, skipped, failed } = summarize(entries);
75
+ const { success, skipped, blocked, failed } = summarize(entries);
68
76
  const actionLabels = {
69
77
  validate: {
70
78
  complete: 'Validation complete',
@@ -94,7 +102,7 @@ function printTaskSummary(entries, warnings, options) {
94
102
  const labels = actionLabels[options.action];
95
103
  let summaryEmoji;
96
104
  let summaryText;
97
- if (failed > 0) {
105
+ if (failed > 0 || blocked > 0) {
98
106
  summaryEmoji = '❌';
99
107
  summaryText = labels.error;
100
108
  process.exitCode = process.exitCode || 1;
@@ -110,6 +118,7 @@ function printTaskSummary(entries, warnings, options) {
110
118
  const parts = [
111
119
  `${summaryEmoji} ${summaryText} - ${success} ${labels.unit}`,
112
120
  `${skipped} skipped`,
121
+ `${blocked} blocked`,
113
122
  `${failed} failed`,
114
123
  ];
115
124
  if (options.action === 'upload' && options.environment) {
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.7.3",
3
- "build": 1,
2
+ "version": "0.7.4",
3
+ "build": 3,
4
4
  "platforms": {
5
5
  "ios": {
6
6
  "minimumVersion": "16.0"
@@ -27,11 +27,11 @@
27
27
  "clients": {
28
28
  "web": {
29
29
  "minimumVersion": "0.7.3",
30
- "minimumBuild": 1
30
+ "minimumBuild": 2
31
31
  },
32
32
  "admin": {
33
33
  "minimumVersion": "0.7.3",
34
- "minimumBuild": 1
34
+ "minimumBuild": 2
35
35
  },
36
36
  "apple": {
37
37
  "minimumVersion": "0.3.10",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "android-games": {
49
49
  "minimumVersion": "0.7.3",
50
- "minimumBuild": 1
50
+ "minimumBuild": 2
51
51
  },
52
52
  "cli": {
53
53
  "minimumVersion": "0.7.3"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playdrop/playdrop-cli",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "Official Playdrop CLI for publishing browser games, creator apps, and AI-generated game assets on playdrop.ai",
5
5
  "homepage": "https://www.playdrop.ai",
6
6
  "repository": {