@hs-x/cli 0.3.6 → 0.4.0

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.
Files changed (211) hide show
  1. package/README.md +126 -0
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +58 -59
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/cli/kit.d.ts.map +1 -1
  6. package/dist/cli/kit.js +1 -1
  7. package/dist/cli/kit.js.map +1 -1
  8. package/dist/cloudflare-auth.d.ts +19 -0
  9. package/dist/cloudflare-auth.d.ts.map +1 -1
  10. package/dist/cloudflare-auth.js +83 -2
  11. package/dist/cloudflare-auth.js.map +1 -1
  12. package/dist/cloudflare-oauth.d.ts.map +1 -1
  13. package/dist/cloudflare-oauth.js +18 -19
  14. package/dist/cloudflare-oauth.js.map +1 -1
  15. package/dist/cloudflare-scoped-resource.d.ts +41 -0
  16. package/dist/cloudflare-scoped-resource.d.ts.map +1 -0
  17. package/dist/cloudflare-scoped-resource.js +74 -0
  18. package/dist/cloudflare-scoped-resource.js.map +1 -0
  19. package/dist/command-catalog.d.ts +13 -0
  20. package/dist/command-catalog.d.ts.map +1 -0
  21. package/dist/command-catalog.js +50 -0
  22. package/dist/command-catalog.js.map +1 -0
  23. package/dist/commands/completion.d.ts.map +1 -1
  24. package/dist/commands/completion.js +287 -47
  25. package/dist/commands/completion.js.map +1 -1
  26. package/dist/commands/connect.d.ts.map +1 -1
  27. package/dist/commands/connect.js +274 -38
  28. package/dist/commands/connect.js.map +1 -1
  29. package/dist/commands/control-plane-read.d.ts +31 -3
  30. package/dist/commands/control-plane-read.d.ts.map +1 -1
  31. package/dist/commands/control-plane-read.js +136 -37
  32. package/dist/commands/control-plane-read.js.map +1 -1
  33. package/dist/commands/deploy-retry.d.ts +20 -0
  34. package/dist/commands/deploy-retry.d.ts.map +1 -0
  35. package/dist/commands/deploy-retry.js +106 -0
  36. package/dist/commands/deploy-retry.js.map +1 -0
  37. package/dist/commands/deploy.d.ts +16 -8
  38. package/dist/commands/deploy.d.ts.map +1 -1
  39. package/dist/commands/deploy.js +1027 -373
  40. package/dist/commands/deploy.js.map +1 -1
  41. package/dist/commands/dev.d.ts +19 -1
  42. package/dist/commands/dev.d.ts.map +1 -1
  43. package/dist/commands/dev.js +1378 -88
  44. package/dist/commands/dev.js.map +1 -1
  45. package/dist/commands/doctor.d.ts.map +1 -1
  46. package/dist/commands/doctor.js +27 -10
  47. package/dist/commands/doctor.js.map +1 -1
  48. package/dist/commands/flags.d.ts +9 -4
  49. package/dist/commands/flags.d.ts.map +1 -1
  50. package/dist/commands/flags.js +208 -80
  51. package/dist/commands/flags.js.map +1 -1
  52. package/dist/commands/help-command.d.ts.map +1 -1
  53. package/dist/commands/help-command.js +177 -86
  54. package/dist/commands/help-command.js.map +1 -1
  55. package/dist/commands/init.d.ts.map +1 -1
  56. package/dist/commands/init.js +5 -0
  57. package/dist/commands/init.js.map +1 -1
  58. package/dist/commands/link.d.ts.map +1 -1
  59. package/dist/commands/link.js +74 -15
  60. package/dist/commands/link.js.map +1 -1
  61. package/dist/commands/project.d.ts +107 -0
  62. package/dist/commands/project.d.ts.map +1 -0
  63. package/dist/commands/project.js +586 -0
  64. package/dist/commands/project.js.map +1 -0
  65. package/dist/commands/secrets.d.ts +2 -2
  66. package/dist/commands/status.d.ts.map +1 -1
  67. package/dist/commands/status.js +15 -5
  68. package/dist/commands/status.js.map +1 -1
  69. package/dist/commands/validate.d.ts +19 -0
  70. package/dist/commands/validate.d.ts.map +1 -1
  71. package/dist/commands/validate.js +26 -7
  72. package/dist/commands/validate.js.map +1 -1
  73. package/dist/constants.d.ts +1 -1
  74. package/dist/constants.js +1 -1
  75. package/dist/control-plane-tenant-provisioning.d.ts +24 -0
  76. package/dist/control-plane-tenant-provisioning.d.ts.map +1 -0
  77. package/dist/control-plane-tenant-provisioning.js +85 -0
  78. package/dist/control-plane-tenant-provisioning.js.map +1 -0
  79. package/dist/deploy-targets.d.ts +24 -0
  80. package/dist/deploy-targets.d.ts.map +1 -0
  81. package/dist/deploy-targets.js +38 -0
  82. package/dist/deploy-targets.js.map +1 -0
  83. package/dist/dev/account-selection.d.ts +16 -0
  84. package/dist/dev/account-selection.d.ts.map +1 -0
  85. package/dist/dev/account-selection.js +22 -0
  86. package/dist/dev/account-selection.js.map +1 -0
  87. package/dist/dev/browser-log-bridge.d.ts +32 -0
  88. package/dist/dev/browser-log-bridge.d.ts.map +1 -0
  89. package/dist/dev/browser-log-bridge.js +156 -0
  90. package/dist/dev/browser-log-bridge.js.map +1 -0
  91. package/dist/dev/compat-shim.d.ts +3 -3
  92. package/dist/dev/compat-shim.d.ts.map +1 -1
  93. package/dist/dev/compat-shim.js +29 -4
  94. package/dist/dev/compat-shim.js.map +1 -1
  95. package/dist/dev/component-selection.d.ts +25 -0
  96. package/dist/dev/component-selection.d.ts.map +1 -0
  97. package/dist/dev/component-selection.js +79 -0
  98. package/dist/dev/component-selection.js.map +1 -0
  99. package/dist/dev/console-capture.d.ts +18 -0
  100. package/dist/dev/console-capture.d.ts.map +1 -0
  101. package/dist/dev/console-capture.js +57 -0
  102. package/dist/dev/console-capture.js.map +1 -0
  103. package/dist/dev/event-bus.d.ts +1 -1
  104. package/dist/dev/event-bus.js +1 -1
  105. package/dist/dev/frontend-log-relay.d.ts +27 -0
  106. package/dist/dev/frontend-log-relay.d.ts.map +1 -0
  107. package/dist/dev/frontend-log-relay.js +102 -0
  108. package/dist/dev/frontend-log-relay.js.map +1 -0
  109. package/dist/dev/invoke.d.ts +3 -4
  110. package/dist/dev/invoke.d.ts.map +1 -1
  111. package/dist/dev/invoke.js +9 -4
  112. package/dist/dev/invoke.js.map +1 -1
  113. package/dist/dev/keyboard.d.ts +1 -0
  114. package/dist/dev/keyboard.d.ts.map +1 -1
  115. package/dist/dev/keyboard.js +9 -3
  116. package/dist/dev/keyboard.js.map +1 -1
  117. package/dist/dev/log-server.js +3 -0
  118. package/dist/dev/log-server.js.map +1 -1
  119. package/dist/dev/loopback-http-listener.d.ts +18 -0
  120. package/dist/dev/loopback-http-listener.d.ts.map +1 -0
  121. package/dist/dev/loopback-http-listener.js +91 -0
  122. package/dist/dev/loopback-http-listener.js.map +1 -0
  123. package/dist/dev/managed-session.d.ts +124 -0
  124. package/dist/dev/managed-session.d.ts.map +1 -0
  125. package/dist/dev/managed-session.js +597 -0
  126. package/dist/dev/managed-session.js.map +1 -0
  127. package/dist/dev/port-process.d.ts +21 -0
  128. package/dist/dev/port-process.d.ts.map +1 -0
  129. package/dist/dev/port-process.js +145 -0
  130. package/dist/dev/port-process.js.map +1 -0
  131. package/dist/dev/session-manager.d.ts +11 -1
  132. package/dist/dev/session-manager.d.ts.map +1 -1
  133. package/dist/dev/session-manager.js +65 -14
  134. package/dist/dev/session-manager.js.map +1 -1
  135. package/dist/dev/stream-event-format.d.ts +14 -0
  136. package/dist/dev/stream-event-format.d.ts.map +1 -0
  137. package/dist/dev/stream-event-format.js +262 -0
  138. package/dist/dev/stream-event-format.js.map +1 -0
  139. package/dist/dev/stream-filter.d.ts.map +1 -1
  140. package/dist/dev/stream-filter.js +16 -1
  141. package/dist/dev/stream-filter.js.map +1 -1
  142. package/dist/dev/stream-renderer.d.ts +4 -2
  143. package/dist/dev/stream-renderer.d.ts.map +1 -1
  144. package/dist/dev/stream-renderer.js +224 -84
  145. package/dist/dev/stream-renderer.js.map +1 -1
  146. package/dist/errors-registry.d.ts.map +1 -1
  147. package/dist/errors-registry.js +246 -0
  148. package/dist/errors-registry.js.map +1 -1
  149. package/dist/help.d.ts.map +1 -1
  150. package/dist/help.js +20 -3
  151. package/dist/help.js.map +1 -1
  152. package/dist/hubspot-app-logs.d.ts +38 -5
  153. package/dist/hubspot-app-logs.d.ts.map +1 -1
  154. package/dist/hubspot-app-logs.js +88 -17
  155. package/dist/hubspot-app-logs.js.map +1 -1
  156. package/dist/hubspot-auth.d.ts +19 -1
  157. package/dist/hubspot-auth.d.ts.map +1 -1
  158. package/dist/hubspot-auth.js +228 -83
  159. package/dist/hubspot-auth.js.map +1 -1
  160. package/dist/hubspot-developer-client.d.ts +87 -0
  161. package/dist/hubspot-developer-client.d.ts.map +1 -1
  162. package/dist/hubspot-developer-client.js +232 -1
  163. package/dist/hubspot-developer-client.js.map +1 -1
  164. package/dist/hubspot-project-archive.d.ts +21 -0
  165. package/dist/hubspot-project-archive.d.ts.map +1 -0
  166. package/dist/hubspot-project-archive.js +281 -0
  167. package/dist/hubspot-project-archive.js.map +1 -0
  168. package/dist/hubspot-project.d.ts +32 -0
  169. package/dist/hubspot-project.d.ts.map +1 -0
  170. package/dist/hubspot-project.js +269 -0
  171. package/dist/hubspot-project.js.map +1 -0
  172. package/dist/local-project-picker.js +1 -1
  173. package/dist/local-project-picker.js.map +1 -1
  174. package/dist/machine-artifact.d.ts +16 -0
  175. package/dist/machine-artifact.d.ts.map +1 -0
  176. package/dist/machine-artifact.js +18 -0
  177. package/dist/machine-artifact.js.map +1 -0
  178. package/dist/machine-id.d.ts +15 -0
  179. package/dist/machine-id.d.ts.map +1 -1
  180. package/dist/machine-id.js +53 -0
  181. package/dist/machine-id.js.map +1 -1
  182. package/dist/mutation-consent.d.ts +20 -0
  183. package/dist/mutation-consent.d.ts.map +1 -0
  184. package/dist/mutation-consent.js +9 -0
  185. package/dist/mutation-consent.js.map +1 -0
  186. package/dist/output-context.d.ts +3 -0
  187. package/dist/output-context.d.ts.map +1 -1
  188. package/dist/output-context.js +7 -1
  189. package/dist/output-context.js.map +1 -1
  190. package/dist/prompt.d.ts.map +1 -1
  191. package/dist/prompt.js +123 -48
  192. package/dist/prompt.js.map +1 -1
  193. package/dist/reporter/index.js +1 -1
  194. package/dist/reporter/index.js.map +1 -1
  195. package/dist/reporter/style.d.ts.map +1 -1
  196. package/dist/reporter/style.js +5 -0
  197. package/dist/reporter/style.js.map +1 -1
  198. package/dist/services/hubspot-dev-client.js +3 -0
  199. package/dist/services/hubspot-dev-client.js.map +1 -1
  200. package/dist/services/telemetry.d.ts +1 -2
  201. package/dist/services/telemetry.d.ts.map +1 -1
  202. package/dist/services/telemetry.js +1 -10
  203. package/dist/services/telemetry.js.map +1 -1
  204. package/dist/telemetry.d.ts.map +1 -1
  205. package/dist/telemetry.js +1 -7
  206. package/dist/telemetry.js.map +1 -1
  207. package/dist/tenant-data-plane-provisioning.d.ts +35 -0
  208. package/dist/tenant-data-plane-provisioning.d.ts.map +1 -0
  209. package/dist/tenant-data-plane-provisioning.js +38 -0
  210. package/dist/tenant-data-plane-provisioning.js.map +1 -0
  211. package/package.json +14 -12
@@ -1,6 +1,7 @@
1
1
  import * as Command from '@effect/cli/Command';
2
2
  import * as Options from '@effect/cli/Options';
3
3
  import { HttpClientRequest, Schema, schemas } from '@hs-x/types';
4
+ import { fnv1a32 } from '@hs-x/types/fnv1a';
4
5
  import { Config, Effect, Option } from 'effect';
5
6
  import { isLinked } from '../account-store.js';
6
7
  import { auditExplorer, filterAuditRows, promptAuditTimeRange, renderAuditDetail, } from '../audit-interactive.js';
@@ -9,7 +10,9 @@ import { CliError, cliError, exitWith } from '../cli-error.js';
9
10
  import { accountIdOption, controlPlaneUrlOption, jsonOption, projectIdOption, runHandler, userIdOption, } from '../cli/kit.js';
10
11
  import { DEFAULT_CONTROL_PLANE_URL } from '../config.js';
11
12
  import { executeCliHttpEffect } from '../effect-http.js';
12
- import { fetchHubSpotLogsPayload, mergeLogRows, parseHubSpotLogSystemTypes, resolveHubSpotLogsSource, } from '../hubspot-app-logs.js';
13
+ import { fetchHubSpotLogsPayload, mergeLogRows, parseHubSpotLogSystemTypes, resolveDirectHubSpotLogsContext, resolveHubSpotLogsSource, } from '../hubspot-app-logs.js';
14
+ import { DirectHubSpotAccountResolutionError, } from '../hubspot-developer-client.js';
15
+ import { detectLocalProjectMode } from '../hubspot-project.js';
13
16
  import { promptInteractiveLogsSelection } from '../logs-interactive.js';
14
17
  import { readProjectBinding } from '../project-binding.js';
15
18
  import { ProjectPickerCancelled, discoverProjects, resolveProjectSelection, } from '../project-picker.js';
@@ -203,21 +206,13 @@ function stableLogRowKey(row) {
203
206
  function orderLogRowsAscending(rows) {
204
207
  return [...rows].sort((a, b) => (readString(a, 'timestamp') ?? '').localeCompare(readString(b, 'timestamp') ?? ''));
205
208
  }
206
- function hashString(value) {
207
- let hash = 2166136261;
208
- for (let index = 0; index < value.length; index += 1) {
209
- hash ^= value.charCodeAt(index);
210
- hash = Math.imul(hash, 16777619);
211
- }
212
- return hash >>> 0;
213
- }
214
209
  function applyLogFilters(rows, filters, sampleRate) {
215
210
  return rows.filter((row) => {
216
211
  if (!filters.every((filter) => compareLogFilter(row, filter)))
217
212
  return false;
218
213
  if (sampleRate === undefined || sampleRate >= 1)
219
214
  return true;
220
- return hashString(stableLogRowKey(row)) / 0xffffffff <= sampleRate;
215
+ return fnv1a32(stableLogRowKey(row)) / 0xffffffff <= sampleRate;
221
216
  });
222
217
  }
223
218
  function asLogsPayload(body) {
@@ -1075,6 +1070,15 @@ const logsOptions = {
1075
1070
  appId: Options.text('app-id').pipe(Options.optional),
1076
1071
  type: Options.text('type').pipe(Options.repeated),
1077
1072
  errorsOnly: Options.boolean('errors-only').pipe(Options.withDefault(false)),
1073
+ functionName: Options.text('function').pipe(Options.optional),
1074
+ latest: Options.boolean('latest').pipe(Options.withAlias('l'), Options.withDefault(false)),
1075
+ compact: Options.boolean('compact').pipe(Options.withDefault(false)),
1076
+ };
1077
+ const projectLogsOptions = {
1078
+ ...logsOptions,
1079
+ account: Options.text('account').pipe(Options.withAlias('a'), Options.optional),
1080
+ config: Options.text('config').pipe(Options.withAlias('c'), Options.optional),
1081
+ useEnv: Options.boolean('use-env').pipe(Options.withDefault(false)),
1078
1082
  };
1079
1083
  const LOG_SELECTOR_FLAGS = [
1080
1084
  '--source',
@@ -1092,6 +1096,10 @@ const LOG_SELECTOR_FLAGS = [
1092
1096
  '--follow',
1093
1097
  '--tail',
1094
1098
  '--errors-only',
1099
+ '--function',
1100
+ '--latest',
1101
+ '-l',
1102
+ '--compact',
1095
1103
  ];
1096
1104
  function hasExplicitLogSelector(argv = process.argv.slice(2)) {
1097
1105
  return argv.some((arg) => LOG_SELECTOR_FLAGS.some((flag) => arg === flag || arg.startsWith(`${flag}=`)));
@@ -1117,6 +1125,55 @@ function parseLogSource(value) {
1117
1125
  return { ok: false, message: `Invalid --source "${value}". Use all, workers, or hubspot.` };
1118
1126
  }
1119
1127
  export const logsCmd = Command.make('logs', logsOptions, (opts) => runHandler('logs', { json: opts.json }, logsBody(opts)));
1128
+ export const projectLogsCmd = Command.make('logs', projectLogsOptions, (opts) => runHandler('project logs', { json: opts.json }, logsBody(opts, {
1129
+ commandName: 'project logs',
1130
+ directHubSpotArgv: projectLogsAccountArgv(opts),
1131
+ })));
1132
+ function projectLogsAccountArgv(opts) {
1133
+ const argv = ['project', 'logs'];
1134
+ const account = Option.getOrUndefined(opts.account);
1135
+ const config = Option.getOrUndefined(opts.config);
1136
+ if (account)
1137
+ argv.push('--account', account);
1138
+ if (config)
1139
+ argv.push('--config', config);
1140
+ if (opts.useEnv)
1141
+ argv.push('--use-env');
1142
+ return argv;
1143
+ }
1144
+ function directHubSpotLogsError(cause) {
1145
+ if (cause instanceof CliError)
1146
+ return cause;
1147
+ if (cause instanceof DirectHubSpotAccountResolutionError) {
1148
+ return new CliError({
1149
+ code: cause.code,
1150
+ message: cause.message,
1151
+ context: { hint: cause.hint },
1152
+ exitCode: 1,
1153
+ });
1154
+ }
1155
+ return new CliError({
1156
+ code: 'HSX_E_LOGS_UNAVAILABLE',
1157
+ message: cause instanceof Error ? cause.message : String(cause),
1158
+ exitCode: 1,
1159
+ });
1160
+ }
1161
+ function directHubSpotAccountRows(account) {
1162
+ const label = account.name ? `${account.name} (${account.id})` : account.id;
1163
+ return [
1164
+ {
1165
+ key: 'hubspot-account',
1166
+ value: label,
1167
+ detail: `source=${account.source} selectedBy=${account.selectedBy} authReady=${String(account.authReady)}`,
1168
+ },
1169
+ ...(account.configSource
1170
+ ? [{ key: 'hubspot-config', value: account.configSource }]
1171
+ : []),
1172
+ ...(account.defaultSource
1173
+ ? [{ key: 'hubspot-default', value: account.defaultSource }]
1174
+ : []),
1175
+ ];
1176
+ }
1120
1177
  function resolveAccountId(opts, localAccountId) {
1121
1178
  return Effect.gen(function* () {
1122
1179
  const explicit = yield* resolveFromOptionOrEnv(opts.accountId, 'HSX_ACCOUNT_ID');
@@ -1148,14 +1205,18 @@ function withWorkersErrorHint(error) {
1148
1205
  }
1149
1206
  function logsBody(opts, context = {}) {
1150
1207
  return Effect.gen(function* () {
1208
+ const commandName = context.commandName ?? 'logs';
1151
1209
  const cwd = yield* Cwd;
1210
+ const projectMode = yield* Effect.promise(() => detectLocalProjectMode(cwd));
1211
+ const directHubSpotProject = projectMode === 'hubspot-direct';
1152
1212
  const localProjectBinding = yield* Effect.promise(() => readProjectBinding(cwd));
1153
- const requestedSource = parseLogSource(Option.getOrUndefined(opts.source));
1213
+ const requestedSourceValue = Option.getOrUndefined(opts.source) ?? (directHubSpotProject ? 'hubspot' : undefined);
1214
+ const requestedSource = parseLogSource(requestedSourceValue);
1154
1215
  const shouldResolveWorkersProject = requestedSource.ok && requestedSource.wantWorkers;
1155
1216
  const projectSelection = shouldResolveWorkersProject
1156
1217
  ? (context.projectSelection ??
1157
1218
  (yield* resolveOperationProjectForRead(opts, {
1158
- command: 'logs',
1219
+ command: commandName,
1159
1220
  message: 'Which project do you want logs for?',
1160
1221
  emptyMessage: 'No projects yet. Run `hs-x deploy` from a project to create one.',
1161
1222
  })))
@@ -1163,20 +1224,23 @@ function logsBody(opts, context = {}) {
1163
1224
  const currentProjectId = projectSelection?.projectId ??
1164
1225
  (yield* resolveProjectId(opts)) ??
1165
1226
  localProjectBinding?.projectId;
1166
- const accountId = projectSelection?.accountId ??
1167
- (yield* resolveAccountId(opts, localProjectBinding?.accountId));
1227
+ const accountId = directHubSpotProject
1228
+ ? undefined
1229
+ : (projectSelection?.accountId ??
1230
+ (yield* resolveAccountId(opts, localProjectBinding?.accountId)));
1168
1231
  const controlPlaneUrl = yield* resolveControlPlaneUrl(opts.controlPlaneUrl);
1169
1232
  const userId = yield* resolveUserId(opts);
1170
1233
  const shouldPrompt = !opts.json &&
1171
1234
  isInteractive() &&
1235
+ !directHubSpotProject &&
1172
1236
  (context.forceInteractiveSetup === true || !hasExplicitLogSelector());
1173
1237
  let interactiveSelection = undefined;
1174
1238
  if (shouldPrompt) {
1175
1239
  if (!accountId) {
1176
- return yield* failBespoke(false, 'logs', 'Logs require an HS-X account: run `hs-x login`, or pass --account-id / HSX_ACCOUNT_ID.');
1240
+ return yield* failBespoke(false, commandName, 'Logs require an HS-X account: run `hs-x login`, or pass --account-id / HSX_ACCOUNT_ID.');
1177
1241
  }
1178
1242
  if (!currentProjectId) {
1179
- return yield* failBespoke(false, 'logs', `Logs need a project: ${PROJECT_ID_HINT}.`);
1243
+ return yield* failBespoke(false, commandName, `Logs need a project: ${PROJECT_ID_HINT}.`);
1180
1244
  }
1181
1245
  interactiveSelection = yield* Effect.promise(() => promptInteractiveLogsSelection({
1182
1246
  projects: projectSelection?.projects ?? [],
@@ -1190,46 +1254,67 @@ function logsBody(opts, context = {}) {
1190
1254
  }
1191
1255
  }
1192
1256
  const projectId = interactiveSelection?.projectId ?? currentProjectId;
1193
- const parsedFilters = parseLogFilters(opts.filter);
1257
+ const functionName = Option.getOrUndefined(opts.functionName);
1258
+ const parsedFilters = parseLogFilters([
1259
+ ...opts.filter,
1260
+ ...(functionName ? [`target=${functionName}`] : []),
1261
+ ]);
1194
1262
  if (!parsedFilters.ok) {
1195
- return yield* failBespoke(opts.json, 'logs', parsedFilters.message);
1263
+ return yield* failBespoke(opts.json, commandName, parsedFilters.message);
1196
1264
  }
1197
1265
  const parsedSample = parseSampleRate(Option.getOrUndefined(opts.sample));
1198
1266
  if (!parsedSample.ok) {
1199
- return yield* failBespoke(opts.json, 'logs', parsedSample.message);
1267
+ return yield* failBespoke(opts.json, commandName, parsedSample.message);
1200
1268
  }
1201
1269
  const follow = interactiveSelection?.follow ?? opts.follow;
1202
1270
  const errorsOnly = interactiveSelection?.errorsOnly ?? opts.errorsOnly;
1271
+ const requestedTypes = functionName ? [...opts.type, 'serverless'] : opts.type;
1203
1272
  const parsedSystemTypes = interactiveSelection?.systemTypes
1204
1273
  ? { ok: true, systemTypes: interactiveSelection.systemTypes }
1205
- : parseHubSpotLogSystemTypes(opts.type);
1274
+ : parseHubSpotLogSystemTypes(requestedTypes);
1206
1275
  if (!parsedSystemTypes.ok) {
1207
- return yield* failBespoke(opts.json, 'logs', parsedSystemTypes.message);
1276
+ return yield* failBespoke(opts.json, commandName, parsedSystemTypes.message);
1208
1277
  }
1209
1278
  if (follow && opts.json) {
1210
- return yield* failBespoke(opts.json, 'logs', '`hs-x logs --follow`/`--tail` streams terminal output. Omit --json for follow mode.');
1279
+ return yield* failBespoke(opts.json, commandName, '`hs-x logs --follow`/`--tail` streams terminal output. Omit --json for follow mode.');
1211
1280
  }
1212
- const sourceChoice = parseLogSource(interactiveSelection?.source ?? Option.getOrUndefined(opts.source));
1281
+ const sourceChoice = parseLogSource(interactiveSelection?.source ?? requestedSourceValue);
1213
1282
  if (!sourceChoice.ok) {
1214
- return yield* failBespoke(opts.json, 'logs', sourceChoice.message);
1283
+ return yield* failBespoke(opts.json, commandName, sourceChoice.message);
1215
1284
  }
1216
1285
  const { wantWorkers, wantHubspot } = sourceChoice;
1217
1286
  // The two sources resolve independently: Workers Logs need an HS-X account
1218
1287
  // and project id (the control plane mediates the read); HubSpot app logs
1219
- // need only the local `.hs-x/hubspot.json` binding plus PAK credentials.
1288
+ // need an app identity plus a credential-bound HubSpot developer client.
1289
+ // `project logs` resolves that client and its render-safe account together;
1290
+ // legacy `hs-x logs` keeps its existing local binding/config behavior.
1220
1291
  // A missing prerequisite is fatal only when it leaves no source at all.
1221
1292
  const explicitAppId = Option.getOrUndefined(opts.appId);
1222
1293
  const selectedDifferentProject = projectSelection?.projects !== undefined && localProjectBinding?.projectId !== projectId;
1294
+ const hubspotSelectionBlocked = selectedDifferentProject && explicitAppId === undefined
1295
+ ? 'the selected project is not the project in the current directory; pass --app-id to include its HubSpot app logs'
1296
+ : undefined;
1297
+ const directHubSpotLogs = wantHubspot && !hubspotSelectionBlocked && context.directHubSpotArgv
1298
+ ? yield* Effect.tryPromise({
1299
+ try: () => resolveDirectHubSpotLogsContext({
1300
+ argv: context.directHubSpotArgv ?? [],
1301
+ cwd,
1302
+ ...(explicitAppId !== undefined ? { appId: explicitAppId } : {}),
1303
+ }),
1304
+ catch: directHubSpotLogsError,
1305
+ })
1306
+ : undefined;
1223
1307
  const hubspotResolution = wantHubspot
1224
- ? selectedDifferentProject && explicitAppId === undefined
1308
+ ? hubspotSelectionBlocked
1225
1309
  ? {
1226
1310
  ok: false,
1227
- reason: 'the selected project is not the project in the current directory; pass --app-id to include its HubSpot app logs',
1311
+ reason: hubspotSelectionBlocked,
1228
1312
  }
1229
- : yield* Effect.promise(() => resolveHubSpotLogsSource({
1230
- cwd,
1231
- ...(explicitAppId !== undefined ? { appId: explicitAppId } : {}),
1232
- }))
1313
+ : (directHubSpotLogs?.resolution ??
1314
+ (yield* Effect.promise(() => resolveHubSpotLogsSource({
1315
+ cwd,
1316
+ ...(explicitAppId !== undefined ? { appId: explicitAppId } : {}),
1317
+ }))))
1233
1318
  : undefined;
1234
1319
  const hubspotSource = hubspotResolution?.ok
1235
1320
  ? hubspotResolution.source
@@ -1245,15 +1330,15 @@ function logsBody(opts, context = {}) {
1245
1330
  const workersActive = workersReason === undefined;
1246
1331
  if (!workersActive && !hubspotSource) {
1247
1332
  if (wantWorkers && !accountId) {
1248
- return yield* failBespoke(opts.json, 'logs', 'Logs require an HS-X account: run `hs-x login`, or pass --account-id / HSX_ACCOUNT_ID.');
1333
+ return yield* failBespoke(opts.json, commandName, 'Logs require an HS-X account: run `hs-x login`, or pass --account-id / HSX_ACCOUNT_ID.');
1249
1334
  }
1250
1335
  if (wantWorkers && !projectId) {
1251
- return yield* failBespoke(opts.json, 'logs', `Logs need a project: ${PROJECT_ID_HINT}.`);
1336
+ return yield* failBespoke(opts.json, commandName, `Logs need a project: ${PROJECT_ID_HINT}.`);
1252
1337
  }
1253
- return yield* failBespoke(opts.json, 'logs', `HubSpot app logs unavailable: ${hubspotReason ?? 'unknown'}.`);
1338
+ return yield* failBespoke(opts.json, commandName, `HubSpot app logs unavailable: ${hubspotReason ?? 'unknown'}.`);
1254
1339
  }
1255
1340
  const trace = Option.getOrUndefined(opts.trace);
1256
- const limit = clampInt(opts.limit, 1, 200);
1341
+ const limit = clampInt(opts.latest ? 1 : opts.limit, 1, 200);
1257
1342
  const sinceMinutes = clampInt(interactiveSelection?.sinceMinutes ?? opts.sinceMinutes, 1, 7 * 24 * 60);
1258
1343
  const intervalSeconds = clampInt(opts.intervalSeconds, 1, 60);
1259
1344
  const view = trace ? 'trace' : opts.raw ? 'tail' : 'invocations';
@@ -1279,7 +1364,7 @@ function logsBody(opts, context = {}) {
1279
1364
  if (!response.ok) {
1280
1365
  const message = responseMessage(body, response.status, 'Control-plane logs read failed');
1281
1366
  if (strictWorkers) {
1282
- yield* failBespoke(opts.json, 'logs', message);
1367
+ yield* failBespoke(opts.json, commandName, message);
1283
1368
  return { rows: [] };
1284
1369
  }
1285
1370
  return { rows: [], error: message };
@@ -1294,6 +1379,7 @@ function logsBody(opts, context = {}) {
1294
1379
  systemTypes: parsedSystemTypes.systemTypes,
1295
1380
  errorsOnly,
1296
1381
  ...(trace ? { trace } : {}),
1382
+ ...(directHubSpotLogs ? { client: directHubSpotLogs.client } : {}),
1297
1383
  }).catch((cause) => ({
1298
1384
  rows: [],
1299
1385
  error: cause instanceof Error ? cause.message : String(cause),
@@ -1339,10 +1425,11 @@ function logsBody(opts, context = {}) {
1339
1425
  : 'workers-logs';
1340
1426
  const result = {
1341
1427
  ok: !payload.fatalError,
1342
- command: 'logs',
1428
+ command: commandName,
1343
1429
  mode,
1344
1430
  view,
1345
1431
  ...(accountId ? { accountId } : {}),
1432
+ ...(directHubSpotLogs ? { account: directHubSpotLogs.account } : {}),
1346
1433
  ...(projectId ? { projectId } : {}),
1347
1434
  sinceMinutes,
1348
1435
  limit,
@@ -1397,6 +1484,9 @@ function logsBody(opts, context = {}) {
1397
1484
  if (follow) {
1398
1485
  const seen = new Set();
1399
1486
  yield* reporter.header(trace ? `${headerLabel} invocation ${trace}` : `${headerLabel} live`);
1487
+ if (directHubSpotLogs) {
1488
+ yield* reporter.rows(directHubSpotAccountRows(directHubSpotLogs.account));
1489
+ }
1400
1490
  const printNewRows = (rows) => Effect.gen(function* () {
1401
1491
  const fresh = orderLogRowsAscending(rows.filter((row) => {
1402
1492
  const key = stableLogRowKey(row);
@@ -1432,6 +1522,9 @@ function logsBody(opts, context = {}) {
1432
1522
  return;
1433
1523
  }
1434
1524
  yield* reporter.header(trace ? `${headerLabel} invocation ${trace}` : headerLabel);
1525
+ if (directHubSpotLogs) {
1526
+ yield* reporter.rows(directHubSpotAccountRows(directHubSpotLogs.account));
1527
+ }
1435
1528
  yield* emitLogRows(reporter, payload.rows, view, showOrigin);
1436
1529
  yield* reportPartialFailures;
1437
1530
  if (payload.fatalError) {
@@ -1474,6 +1567,9 @@ export function logsBodyForCheckpoint(opts, selection, checkpoint, failure) {
1474
1567
  appId: Option.none(),
1475
1568
  type: [],
1476
1569
  errorsOnly: true,
1570
+ functionName: Option.none(),
1571
+ latest: false,
1572
+ compact: false,
1477
1573
  }, { projectSelection: selection });
1478
1574
  }
1479
1575
  /** In-process command bodies used by the human status drill-down. */
@@ -1493,6 +1589,9 @@ export function logsBodyForStatus(opts, selection) {
1493
1589
  appId: Option.none(),
1494
1590
  type: [],
1495
1591
  errorsOnly: false,
1592
+ functionName: Option.none(),
1593
+ latest: false,
1594
+ compact: false,
1496
1595
  }, { projectSelection: selection, forceInteractiveSetup: true });
1497
1596
  }
1498
1597
  export function routesBodyForStatus(opts, selection) {