@wix/ditto-codegen-public 1.0.441 → 1.0.443

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 (2) hide show
  1. package/dist/out.js +484 -49
  2. package/package.json +2 -2
package/dist/out.js CHANGED
@@ -544,7 +544,15 @@ var require_context = __commonJS({
544
544
  var types_1 = require_types_impl();
545
545
  var dotenv_1 = __importDefault2(require_main());
546
546
  var path_12 = __importDefault2(require("path"));
547
+ var provisioned = {
548
+ CODEGEN_JOB_DELIVERY: process.env.CODEGEN_JOB_DELIVERY,
549
+ CODEGEN_JOB_FETCH_PORT: process.env.CODEGEN_JOB_FETCH_PORT
550
+ };
547
551
  dotenv_1.default.config({ path: path_12.default.join(__dirname, "../.env"), override: true });
552
+ for (const [key, value] of Object.entries(provisioned)) {
553
+ if (value?.trim())
554
+ process.env[key] = value;
555
+ }
548
556
  var HistoryManager = class {
549
557
  constructor() {
550
558
  this.history = [];
@@ -11032,7 +11040,7 @@ var require_CodeGenService = __commonJS({
11032
11040
  "dist/services/CodeGenService.js"(exports2) {
11033
11041
  "use strict";
11034
11042
  Object.defineProperty(exports2, "__esModule", { value: true });
11035
- exports2.CodeGenService = exports2.JobsSortOrder = exports2.TaskKind = exports2.Status = void 0;
11043
+ exports2.CodeGenService = exports2.FETCH_TIMEOUT_MS = exports2.REQUEST_TIMEOUT_MS = exports2.JobsSortOrder = exports2.TaskKind = exports2.Status = void 0;
11036
11044
  exports2.createCodeGenErrorHandler = createCodeGenErrorHandler;
11037
11045
  var http_client_1 = require_index_node();
11038
11046
  var http_1 = require_http_impl();
@@ -11116,6 +11124,14 @@ var require_CodeGenService = __commonJS({
11116
11124
  }
11117
11125
  };
11118
11126
  }
11127
+ exports2.REQUEST_TIMEOUT_MS = 12e4;
11128
+ exports2.FETCH_TIMEOUT_MS = 3e4;
11129
+ function isNotFound(error) {
11130
+ return http_client_1.HttpClient.isHttpError(error) && error.response?.status === 404;
11131
+ }
11132
+ function mapRequest(req, fn) {
11133
+ return typeof req === "function" ? (ctx) => fn(req(ctx)) : fn(req);
11134
+ }
11119
11135
  var CodeGenService = class {
11120
11136
  constructor(projectId, httpClient) {
11121
11137
  this.projectId = projectId;
@@ -11126,15 +11142,16 @@ var require_CodeGenService = __commonJS({
11126
11142
  this.setAccessToken(token);
11127
11143
  return token;
11128
11144
  };
11129
- this.request = async (req) => {
11145
+ this.request = async (req, timeout) => {
11146
+ const build = () => this.withTimeout(this.withAuth(req), timeout);
11130
11147
  try {
11131
- return await this.httpClient.request(this.withAuth(req));
11148
+ return await this.httpClient.request(build());
11132
11149
  } catch (err) {
11133
11150
  if (!this.isUnauthorized(err))
11134
11151
  throw err;
11135
11152
  logger_12.logger.warn("[CodeGenService] 401 received, refreshing token and retrying once");
11136
11153
  await this.refreshAccessToken();
11137
- return await this.httpClient.request(this.withAuth(req));
11154
+ return await this.httpClient.request(build());
11138
11155
  }
11139
11156
  };
11140
11157
  this.fetchPendingJobs = async () => {
@@ -11145,10 +11162,19 @@ var require_CodeGenService = __commonJS({
11145
11162
  paging: { limit: 1 },
11146
11163
  sortOrder: types_1.JobsSortOrder.CREATED_AT_ASC
11147
11164
  } : void 0;
11148
- const resp = await this.request((0, http_1.listProjectJobs)(filtered ?? { projectId: this.projectId }));
11165
+ const resp = await this.request((0, http_1.listProjectJobs)(filtered ?? { projectId: this.projectId }), exports2.FETCH_TIMEOUT_MS);
11149
11166
  const jobs = resp.data?.jobs ?? [];
11150
11167
  return filtered ? jobs : jobs.filter(this.shouldProcessJob);
11151
11168
  };
11169
+ this.findJob = async (jobId) => {
11170
+ try {
11171
+ return await this.getJob(jobId, exports2.FETCH_TIMEOUT_MS);
11172
+ } catch (error) {
11173
+ if (isNotFound(error))
11174
+ return null;
11175
+ throw error;
11176
+ }
11177
+ };
11152
11178
  this.shouldProcessJob = (job) => {
11153
11179
  if (job.status !== types_1.Status.PENDING)
11154
11180
  return false;
@@ -11279,11 +11305,11 @@ var require_CodeGenService = __commonJS({
11279
11305
  }
11280
11306
  }));
11281
11307
  };
11282
- this.getJob = async (jobId) => {
11308
+ this.getJob = async (jobId, timeout) => {
11283
11309
  const resp = await this.request((0, http_1.getJob)({
11284
11310
  projectId: this.projectId,
11285
11311
  jobId
11286
- }));
11312
+ }), timeout);
11287
11313
  return resp.data?.job ?? null;
11288
11314
  };
11289
11315
  this.runContext = (ctx) => ({
@@ -11329,6 +11355,7 @@ var require_CodeGenService = __commonJS({
11329
11355
  const serverUrl = process.env.CODEGEN_SERVER_BASE_URL || "https://manage.wix.com";
11330
11356
  this.httpClient = (0, http_client_1.createHttpClient)({
11331
11357
  baseURL: serverUrl,
11358
+ timeout: exports2.REQUEST_TIMEOUT_MS,
11332
11359
  errorHandler: createCodeGenErrorHandler(() => this.httpClient)
11333
11360
  });
11334
11361
  }
@@ -11346,14 +11373,16 @@ var require_CodeGenService = __commonJS({
11346
11373
  return error.response?.status === 401;
11347
11374
  }
11348
11375
  withAuth(req) {
11349
- const authHeader = { authorization: `Bearer ${this.accessToken}` };
11350
- if (typeof req === "function") {
11351
- return (ctx) => {
11352
- const r = req(ctx);
11353
- return { ...r, headers: { ...r.headers ?? {}, ...authHeader } };
11354
- };
11355
- }
11356
- return { ...req, headers: { ...req.headers ?? {}, ...authHeader } };
11376
+ return mapRequest(req, (r) => ({
11377
+ ...r,
11378
+ headers: {
11379
+ ...r.headers ?? {},
11380
+ authorization: `Bearer ${this.accessToken}`
11381
+ }
11382
+ }));
11383
+ }
11384
+ withTimeout(req, timeout) {
11385
+ return timeout === void 0 ? req : mapRequest(req, (r) => ({ ...r, timeout }));
11357
11386
  }
11358
11387
  };
11359
11388
  exports2.CodeGenService = CodeGenService;
@@ -14682,6 +14711,31 @@ For ANY use of a Wix Stores API (products, variants, inventory, collections/cate
14682
14711
  }
14683
14712
  });
14684
14713
 
14714
+ // ../codegen-common-logic/dist/rules/vertical-installation.js
14715
+ var require_vertical_installation = __commonJS({
14716
+ "../codegen-common-logic/dist/rules/vertical-installation.js"(exports2) {
14717
+ "use strict";
14718
+ Object.defineProperty(exports2, "__esModule", { value: true });
14719
+ exports2.VERTICAL_INSTALLATION_RULES = void 0;
14720
+ exports2.VERTICAL_INSTALLATION_RULES = `# Vertical Installation Rules
14721
+
14722
+ Code written against a Wix app this site doesn't have compiles, saves, and then does nothing.
14723
+
14724
+ For ANY use of a Wix vertical SDK (Stores, eCommerce, Bookings, Restaurants, Pricing Plans, Gift Cards, Blog, Events, Portfolio, Members Area, Forms, Loyalty Program, Donations):
14725
+
14726
+ 1. **You MUST invoke the \`wix-site-installed-apps\` skill before your FIRST write of any kind** \u2014 not merely before the vertical code itself. The build workflow tells you to write each piece as soon as it is ready, so a dashboard shell written before you reach the vertical part is still work rule 2 will make you undo. Name the apps this request needs, by product name; ask in one call and reuse the answer.
14727
+ 2. **If any app comes back \`installed: false\`, STOP. Write no code at all** \u2014 not for that app, and not for the rest of the request either. Your reply is **one** short message to the site owner, naming the app and which parts of the request you did not attempt, and nothing else. Write the outcome, never the mechanism: no rule, no skill, no check or verdict, no app id or endpoint, and no sentence announcing that you are stopping \u2014 the user doesn't know what any of that is, and reading it as an error is the likeliest outcome. \\"Wix Events isn't installed on your site, so I couldn't build the upcoming-events page. Install Wix Events from your dashboard, then ask again and I'll build it.\\" \u2014 that shape, once. **If you have already created or modified files in this session, undo those changes before replying \u2014 path by path, naming each file you wrote, never a bulk revert of the working tree (\`git clean\`, \`git checkout .\`, \`git reset --hard\`, or any "discard all changes"), which also destroys work that was there before you** \u2014 a half-built app that saves is worse than either outcome this rule chooses between.
14728
+ 3. **Do not soften rule 2.** No partial build, no stub, no CMS collection standing in for the app, no "for when they install it" path, and no offering to build the parts that would work. The site owner installs the app, then asks again. **Where the OUTPUT MESSAGE instruction says to build the feature and list installing a dependent app as a "next step", this rule wins** \u2014 for a missing app there is no build, so there is no next step to list against it.
14729
+ 4. **\`installed: null\`, or the check itself failing: build, and say the app may need installing.** A verdict you can't trust never stops anything. The exception is a \`null\` carrying \`"reason":"not-installable"\` \u2014 the check refuses that name because there is no app behind it to install: build, and name nothing. Some plain \`null\`s are platform features with nothing to install either \u2014 contacts, coupons, automations and triggered emails, login and logged-in-only gating \u2014 so build and name nothing for those too.
14730
+ 5. **This outranks the plan.** A blueprint summary or extension description naming a missing app was written without knowing the site; rule 2 wins, and your reply says which planned parts you left unbuilt.
14731
+ 6. **Never install an app** \u2014 it adds pages, a cart, and checkout to the user's live site.
14732
+
14733
+ CMS is exempt: \`wix-site-data-collection\` installs it, so CMS work is never blocked and CMS is never named as missing.
14734
+
14735
+ Two names answer nothing: **"Wix CMS"** (the published id is a variant nobody can install) and **"Wix eCommerce"** (cart and checkout have no standalone install \u2014 they arrive with a catalog app). The check refuses both, answering \`installed: null\` with \`"reason":"not-installable"\`, so asking is harmless but decides nothing. For an eCommerce feature, gate on the catalog app it needs instead \u2014 Wix Stores, Wix Bookings, or the specific Wix Restaurants app \u2014 and never tell the user to install eCommerce or CMS.`;
14736
+ }
14737
+ });
14738
+
14685
14739
  // ../codegen-common-logic/dist/rules/index.js
14686
14740
  var require_rules = __commonJS({
14687
14741
  "../codegen-common-logic/dist/rules/index.js"(exports2) {
@@ -14705,6 +14759,7 @@ var require_rules = __commonJS({
14705
14759
  Object.defineProperty(exports2, "__esModule", { value: true });
14706
14760
  __exportStar2(require_site_data_layer(), exports2);
14707
14761
  __exportStar2(require_stores_catalog_version(), exports2);
14762
+ __exportStar2(require_vertical_installation(), exports2);
14708
14763
  }
14709
14764
  });
14710
14765
 
@@ -14718,6 +14773,10 @@ var require_site_environment = __commonJS({
14718
14773
  exports2.SITE_ENVIRONMENT_IDENTITY = "You generate code for the user's own live Wix site \u2014 to the user it's just their site, not a separate app they're aware of. Your changes are saved and applied to the site automatically; there is no separate release step.";
14719
14774
  exports2.SITE_CODEGEN_RULES = [
14720
14775
  { filename: "site-data-layer-rules.md", content: rules_1.SITE_DATA_LAYER_RULES },
14776
+ {
14777
+ filename: "vertical-installation-rules.md",
14778
+ content: rules_1.VERTICAL_INSTALLATION_RULES
14779
+ },
14721
14780
  {
14722
14781
  filename: "stores-catalog-version-rules.md",
14723
14782
  content: rules_1.STORES_CATALOG_VERSION_RULES
@@ -16508,11 +16567,13 @@ var require_utils9 = __commonJS({
16508
16567
  "dist/utils.js"(exports2) {
16509
16568
  "use strict";
16510
16569
  Object.defineProperty(exports2, "__esModule", { value: true });
16511
- exports2.errorStack = void 0;
16570
+ exports2.errorStack = exports2.delay = void 0;
16512
16571
  exports2.serializeError = serializeError;
16513
16572
  exports2.updateAllRunningTasks = updateAllRunningTasks;
16514
16573
  var types_1 = require_types_impl();
16515
16574
  var codeGenerationService_12 = require_codeGenerationService();
16575
+ var delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
16576
+ exports2.delay = delay;
16516
16577
  var errorStack = (error) => error instanceof Error ? error.stack ?? error.message : String(error);
16517
16578
  exports2.errorStack = errorStack;
16518
16579
  function serializeError(error) {
@@ -23760,7 +23821,7 @@ var require_skills_installer = __commonJS({
23760
23821
  const nested = discoverSkills((0, path_12.join)(packageRoot, "skills"));
23761
23822
  return nested.length > 0 ? nested : discoverSkills(packageRoot);
23762
23823
  }
23763
- var delay2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
23824
+ var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
23764
23825
  async function applySkillsOverride(outputPath, log) {
23765
23826
  const skillsOverride = process.env.SKILLS_OVERRIDE_BRANCH;
23766
23827
  if (!skillsOverride) {
@@ -23806,7 +23867,7 @@ var require_skills_installer = __commonJS({
23806
23867
  });
23807
23868
  const deadline = Date.now() + timeoutMs;
23808
23869
  while (Date.now() < deadline) {
23809
- await delay2(intervalMs);
23870
+ await delay(intervalMs);
23810
23871
  state = inspect(state.candidates);
23811
23872
  if (state.ready) {
23812
23873
  log.info("[Skills] Skills became available", { present: state.present });
@@ -24213,6 +24274,367 @@ var require_plugin_artifacts = __commonJS({
24213
24274
  }
24214
24275
  });
24215
24276
 
24277
+ // dist/fetch-scheduler.js
24278
+ var require_fetch_scheduler = __commonJS({
24279
+ "dist/fetch-scheduler.js"(exports2) {
24280
+ "use strict";
24281
+ Object.defineProperty(exports2, "__esModule", { value: true });
24282
+ exports2.FetchScheduler = exports2.MAX_AWAITED_JOB_IDS = exports2.MAX_MISSING_JOB_RECHECKS = exports2.ERROR_BACKOFF_MS = exports2.POST_JOB_INTERVAL_MS = exports2.MIN_FETCH_INTERVAL_MS = void 0;
24283
+ var logger_12 = require_logger();
24284
+ var utils_12 = require_utils9();
24285
+ exports2.MIN_FETCH_INTERVAL_MS = 1e3;
24286
+ exports2.POST_JOB_INTERVAL_MS = 3e3;
24287
+ exports2.ERROR_BACKOFF_MS = 5e3;
24288
+ exports2.MAX_MISSING_JOB_RECHECKS = 3;
24289
+ exports2.MAX_AWAITED_JOB_IDS = 4;
24290
+ var FetchScheduler = class {
24291
+ constructor(deps) {
24292
+ this.deps = deps;
24293
+ this.cycle = null;
24294
+ this.checkAgain = false;
24295
+ this.stopped = false;
24296
+ this.nextFetchNotBefore = 0;
24297
+ this.interval = 0;
24298
+ this.awaitedJobIds = /* @__PURE__ */ new Map();
24299
+ }
24300
+ start() {
24301
+ this.stopped = false;
24302
+ }
24303
+ /** A cycle already inside runJob is NOT awaited. A caller that needs exclusivity must
24304
+ * not start another runner until that cycle settles. */
24305
+ stop() {
24306
+ this.stopped = true;
24307
+ }
24308
+ /** Records the nudge and makes sure a cycle is running. It does not wait for the
24309
+ * fetch. A caller held on a slow socket times out and reads that as "machine down",
24310
+ * so retrying is ours to do, not theirs. */
24311
+ nudge(jobId) {
24312
+ if (this.stopped)
24313
+ return;
24314
+ if (jobId) {
24315
+ this.awaitedJobIds.delete(jobId);
24316
+ if (this.awaitedJobIds.size >= exports2.MAX_AWAITED_JOB_IDS) {
24317
+ this.awaitedJobIds.delete(this.awaitedJobIds.keys().next().value);
24318
+ }
24319
+ this.awaitedJobIds.set(jobId, exports2.MAX_MISSING_JOB_RECHECKS);
24320
+ }
24321
+ if (this.cycle) {
24322
+ this.checkAgain = true;
24323
+ return;
24324
+ }
24325
+ this.cycle = this.runUntilIdle().catch((error) => logger_12.logger.error("[JobFetch] Cycle aborted", { error: (0, utils_12.errorStack)(error) }));
24326
+ }
24327
+ async runUntilIdle() {
24328
+ try {
24329
+ do {
24330
+ await this.runSingleJobCycle();
24331
+ } while (this.checkAgain && !this.stopped);
24332
+ } finally {
24333
+ this.cycle = null;
24334
+ }
24335
+ }
24336
+ async runSingleJobCycle() {
24337
+ await this.throttle();
24338
+ if (this.stopped)
24339
+ return;
24340
+ this.checkAgain = false;
24341
+ const due = await this.findRunnableJob();
24342
+ if (!due)
24343
+ return;
24344
+ this.checkAgain = true;
24345
+ if (this.stopped)
24346
+ return;
24347
+ await this.attemptJob(due);
24348
+ }
24349
+ async findRunnableJob() {
24350
+ let jobs;
24351
+ try {
24352
+ jobs = await this.deps.fetchPendingJobs();
24353
+ } catch (error) {
24354
+ this.recordDebt("Fetch failed", error);
24355
+ return null;
24356
+ }
24357
+ this.schedule(exports2.MIN_FETCH_INTERVAL_MS);
24358
+ logger_12.logger.info("[JobFetch] Fetched pending jobs", { count: jobs.length });
24359
+ for (const job of jobs) {
24360
+ if (job.id)
24361
+ this.awaitedJobIds.delete(job.id);
24362
+ }
24363
+ if (jobs.length > 0)
24364
+ return jobs[0];
24365
+ if (this.awaitedJobIds.size === 0)
24366
+ return null;
24367
+ try {
24368
+ return await this.claimNudgedJob();
24369
+ } catch (error) {
24370
+ this.recordDebt("Nudged job lookup failed", error);
24371
+ return null;
24372
+ }
24373
+ }
24374
+ async attemptJob(job) {
24375
+ try {
24376
+ await this.deps.runJob(job);
24377
+ this.schedule(exports2.POST_JOB_INTERVAL_MS);
24378
+ } catch (error) {
24379
+ this.recordDebt("Job run failed", error);
24380
+ }
24381
+ }
24382
+ /** A nudged job the list did not return. Read it by key, which does not wait on the
24383
+ * index. A key read is still a replica read, so a job the server does not know about
24384
+ * yet keeps a small budget of re-checks instead of one answer settling it. */
24385
+ async claimNudgedJob() {
24386
+ for (const jobId of [...this.awaitedJobIds.keys()]) {
24387
+ if (this.stopped)
24388
+ return null;
24389
+ const job = await this.deps.findJob(jobId);
24390
+ if (job) {
24391
+ this.awaitedJobIds.delete(jobId);
24392
+ if (this.deps.canRunJob(job))
24393
+ return job;
24394
+ continue;
24395
+ }
24396
+ const rechecksLeft = this.awaitedJobIds.get(jobId);
24397
+ if (rechecksLeft === void 0)
24398
+ continue;
24399
+ if (rechecksLeft > 0) {
24400
+ this.awaitedJobIds.set(jobId, rechecksLeft - 1);
24401
+ this.checkAgain = true;
24402
+ } else {
24403
+ this.awaitedJobIds.delete(jobId);
24404
+ logger_12.logger.warn("[JobFetch] Nudged job does not exist", { jobId });
24405
+ }
24406
+ }
24407
+ return null;
24408
+ }
24409
+ schedule(intervalMs) {
24410
+ this.interval = intervalMs;
24411
+ this.nextFetchNotBefore = Date.now() + intervalMs;
24412
+ }
24413
+ /** Nobody else will retry. We already answered the caller 200. */
24414
+ recordDebt(what, error) {
24415
+ logger_12.logger.error(`[JobFetch] ${what}`, { error: (0, utils_12.errorStack)(error) });
24416
+ this.checkAgain = true;
24417
+ this.schedule(exports2.ERROR_BACKOFF_MS);
24418
+ }
24419
+ async throttle() {
24420
+ const wait = Math.min(this.nextFetchNotBefore - Date.now(), this.interval);
24421
+ if (wait > 0)
24422
+ await (0, utils_12.delay)(wait);
24423
+ }
24424
+ };
24425
+ exports2.FetchScheduler = FetchScheduler;
24426
+ }
24427
+ });
24428
+
24429
+ // dist/nudge-listener-server.js
24430
+ var require_nudge_listener_server = __commonJS({
24431
+ "dist/nudge-listener-server.js"(exports2) {
24432
+ "use strict";
24433
+ Object.defineProperty(exports2, "__esModule", { value: true });
24434
+ exports2.NudgeListenerServer = exports2.BIND_RETRY_DELAY_MS = exports2.BIND_RETRIES = exports2.MAX_LOGGED_PATH_LENGTH = exports2.JOB_ID_PATTERN = exports2.JOB_FETCH_PATH = exports2.JOB_FETCH_PORT = void 0;
24435
+ var node_http_1 = require("node:http");
24436
+ var logger_12 = require_logger();
24437
+ var utils_12 = require_utils9();
24438
+ var fetch_scheduler_1 = require_fetch_scheduler();
24439
+ exports2.JOB_FETCH_PORT = 52402;
24440
+ exports2.JOB_FETCH_PATH = "/_wix/codegen/fetch-jobs";
24441
+ exports2.JOB_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
24442
+ exports2.MAX_LOGGED_PATH_LENGTH = 128;
24443
+ exports2.BIND_RETRIES = 5;
24444
+ exports2.BIND_RETRY_DELAY_MS = 300;
24445
+ var NudgeListenerServer = class {
24446
+ constructor(deps, port = exports2.JOB_FETCH_PORT) {
24447
+ this.port = port;
24448
+ this.server = null;
24449
+ this.stopped = false;
24450
+ this.scheduler = new fetch_scheduler_1.FetchScheduler(deps);
24451
+ }
24452
+ async start() {
24453
+ this.stopped = false;
24454
+ this.scheduler.start();
24455
+ await this.listen();
24456
+ if (this.stopped) {
24457
+ await this.stop();
24458
+ throw new Error("Nudge listener was stopped while binding");
24459
+ }
24460
+ logger_12.logger.info("[JobFetch] Endpoint listening", {
24461
+ port: this.boundPort(),
24462
+ path: exports2.JOB_FETCH_PATH
24463
+ });
24464
+ this.scheduler.nudge();
24465
+ }
24466
+ /** Closes the socket and stops the scheduler; a cycle already inside runJob is NOT
24467
+ * awaited. A caller that needs exclusivity must not start another runner until that
24468
+ * cycle settles. */
24469
+ async stop() {
24470
+ this.stopped = true;
24471
+ this.scheduler.stop();
24472
+ const server = this.server;
24473
+ if (!server)
24474
+ return;
24475
+ this.server = null;
24476
+ server.removeAllListeners("error");
24477
+ server.on("error", (error) => logger_12.logger.warn("[JobFetch] Endpoint error while closing", {
24478
+ error: (0, utils_12.errorStack)(error)
24479
+ }));
24480
+ server.closeAllConnections();
24481
+ await new Promise((resolve) => server.close(() => resolve()));
24482
+ }
24483
+ boundPort() {
24484
+ const address = this.server?.address();
24485
+ return address && typeof address === "object" ? address.port : null;
24486
+ }
24487
+ /** The agent is restarted in a tight `while true` loop, so a bind can land before
24488
+ * the previous process has released the port. Falling straight back to poll there
24489
+ * would strand the machine on the slow path for the whole session. */
24490
+ async listen() {
24491
+ for (let attempt = 0; ; attempt++) {
24492
+ try {
24493
+ return await this.listenOnce();
24494
+ } catch (error) {
24495
+ const inUse = error?.code === "EADDRINUSE" && attempt < exports2.BIND_RETRIES;
24496
+ if (!inUse)
24497
+ throw error;
24498
+ await (0, utils_12.delay)(exports2.BIND_RETRY_DELAY_MS);
24499
+ }
24500
+ }
24501
+ }
24502
+ listenOnce() {
24503
+ return new Promise((resolve, reject) => {
24504
+ const server = (0, node_http_1.createServer)((req, res) => {
24505
+ try {
24506
+ this.handleRequest(req, res);
24507
+ } catch (error) {
24508
+ logger_12.logger.error("[JobFetch] Request handler failed", {
24509
+ error: (0, utils_12.errorStack)(error)
24510
+ });
24511
+ if (res.headersSent)
24512
+ res.destroy();
24513
+ else
24514
+ this.respond(res, 500, { error: "request failed" });
24515
+ }
24516
+ });
24517
+ server.once("error", reject);
24518
+ server.listen(this.port, "0.0.0.0", () => {
24519
+ server.removeListener("error", reject);
24520
+ server.on("error", (error) => {
24521
+ logger_12.logger.error("[JobFetch] Endpoint failed after binding, exiting", {
24522
+ error: (0, utils_12.errorStack)(error)
24523
+ });
24524
+ process.exit(1);
24525
+ });
24526
+ this.server = server;
24527
+ resolve();
24528
+ });
24529
+ });
24530
+ }
24531
+ handleRequest(req, res) {
24532
+ const url = new URL((req.url ?? "").replace(/^\/+/, "/"), "http://localhost");
24533
+ const path = url.pathname.replace(/\/+$/, "");
24534
+ if (req.method !== "GET" || path !== exports2.JOB_FETCH_PATH) {
24535
+ logger_12.logger.warn("[JobFetch] Rejected request", {
24536
+ method: req.method,
24537
+ path: url.pathname.slice(0, exports2.MAX_LOGGED_PATH_LENGTH)
24538
+ });
24539
+ this.respond(res, 404, { error: "not found" });
24540
+ return;
24541
+ }
24542
+ const nudgedId = url.searchParams.get("jobId");
24543
+ const usable = nudgedId === null || exports2.JOB_ID_PATTERN.test(nudgedId);
24544
+ if (!usable) {
24545
+ logger_12.logger.warn("[JobFetch] Nudge carried an unusable jobId", {
24546
+ length: nudgedId.length
24547
+ });
24548
+ }
24549
+ const jobId = usable ? nudgedId ?? void 0 : void 0;
24550
+ this.scheduler.nudge(jobId);
24551
+ logger_12.logger.info("[JobFetch] Nudge accepted", { jobId });
24552
+ this.respond(res, 200, { accepted: true });
24553
+ }
24554
+ respond(res, status, body) {
24555
+ res.writeHead(status, {
24556
+ "Content-Type": "application/json",
24557
+ // A cached response would turn every nudge into a no-op.
24558
+ "Cache-Control": "no-store"
24559
+ });
24560
+ res.end(JSON.stringify(body));
24561
+ }
24562
+ };
24563
+ exports2.NudgeListenerServer = NudgeListenerServer;
24564
+ }
24565
+ });
24566
+
24567
+ // dist/job-fetching.js
24568
+ var require_job_fetching = __commonJS({
24569
+ "dist/job-fetching.js"(exports2) {
24570
+ "use strict";
24571
+ Object.defineProperty(exports2, "__esModule", { value: true });
24572
+ exports2.JobDeliveryMode = void 0;
24573
+ exports2.getJobDeliveryMode = getJobDeliveryMode;
24574
+ exports2.getJobFetchPort = getJobFetchPort;
24575
+ exports2.startJobFetching = startJobFetching;
24576
+ var nudge_listener_server_12 = require_nudge_listener_server();
24577
+ var logger_12 = require_logger();
24578
+ var utils_12 = require_utils9();
24579
+ var JobDeliveryMode;
24580
+ (function(JobDeliveryMode2) {
24581
+ JobDeliveryMode2["POLL"] = "poll";
24582
+ JobDeliveryMode2["PUSH"] = "push";
24583
+ })(JobDeliveryMode || (exports2.JobDeliveryMode = JobDeliveryMode = {}));
24584
+ var DEFAULT_MODE = JobDeliveryMode.POLL;
24585
+ function getJobDeliveryMode() {
24586
+ const mode = process.env.CODEGEN_JOB_DELIVERY?.trim().toLowerCase();
24587
+ const supported = Object.values(JobDeliveryMode).find((candidate) => candidate === mode);
24588
+ if (supported)
24589
+ return supported;
24590
+ if (mode) {
24591
+ logger_12.logger.warn("[Startup] Unrecognized CODEGEN_JOB_DELIVERY, using default", {
24592
+ mode,
24593
+ default: DEFAULT_MODE
24594
+ });
24595
+ }
24596
+ return DEFAULT_MODE;
24597
+ }
24598
+ var MIN_PORT = 1;
24599
+ var MAX_PORT = 65535;
24600
+ function getJobFetchPort() {
24601
+ const configured = process.env.CODEGEN_JOB_FETCH_PORT;
24602
+ if (configured === void 0)
24603
+ return nudge_listener_server_12.JOB_FETCH_PORT;
24604
+ const port = /^\s*\d+\s*$/.test(configured) ? Number(configured) : NaN;
24605
+ if (Number.isInteger(port) && port >= MIN_PORT && port <= MAX_PORT)
24606
+ return port;
24607
+ logger_12.logger.warn("[Startup] Unusable CODEGEN_JOB_FETCH_PORT, using default", {
24608
+ port: configured,
24609
+ default: nudge_listener_server_12.JOB_FETCH_PORT
24610
+ });
24611
+ return nudge_listener_server_12.JOB_FETCH_PORT;
24612
+ }
24613
+ async function startJobFetching(deps) {
24614
+ const mode = getJobDeliveryMode();
24615
+ logger_12.logger.info("[Startup] Job delivery mode", { mode });
24616
+ if (mode === JobDeliveryMode.POLL) {
24617
+ return deps.runPollLoop();
24618
+ }
24619
+ try {
24620
+ await deps.nudgeListenerServer.start();
24621
+ } catch (error) {
24622
+ try {
24623
+ await deps.nudgeListenerServer.stop();
24624
+ } catch (stopError) {
24625
+ logger_12.logger.error("[Startup] Closing the half-started listener failed", {
24626
+ error: (0, utils_12.errorStack)(stopError)
24627
+ });
24628
+ }
24629
+ logger_12.logger.error("[Startup] Push delivery unavailable, falling back to poll", {
24630
+ error: (0, utils_12.errorStack)(error)
24631
+ });
24632
+ return deps.runPollLoop();
24633
+ }
24634
+ }
24635
+ }
24636
+ });
24637
+
24216
24638
  // dist/index.js
24217
24639
  Object.defineProperty(exports, "__esModule", { value: true });
24218
24640
  exports.processJob = processJob;
@@ -24241,61 +24663,67 @@ var subagent_tool_logger_1 = require_subagent_tool_logger();
24241
24663
  var plugin_artifacts_1 = require_plugin_artifacts();
24242
24664
  var config_1 = require_config();
24243
24665
  var environments_1 = require_environments();
24244
- var plugin_host_1 = require_plugin_host();
24245
24666
  var utils_1 = require_utils9();
24667
+ var job_fetching_1 = require_job_fetching();
24668
+ var nudge_listener_server_1 = require_nudge_listener_server();
24669
+ var plugin_host_1 = require_plugin_host();
24670
+ var utils_2 = require_utils9();
24246
24671
  var sessionId = process.env.CODEGEN_SESSION_ID || (0, crypto_1.randomUUID)();
24247
24672
  process.env.CODEGEN_SESSION_ID = sessionId;
24248
24673
  (0, logger_1.initLogger)(sessionId);
24249
24674
  var jobCancellationMonitor = new job_cancellation_monitor_1.JobCancellationMonitor(codeGenerationService_1.codeGenerationService);
24250
24675
  var jobTimeoutMonitor = new job_timeout_monitor_1.JobTimeoutMonitor(codeGenerationService_1.codeGenerationService);
24251
- var delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
24676
+ var POLL_LOG_PREFIX = "[Loop]";
24677
+ var PUSH_LOG_PREFIX = "[JobFetch]";
24252
24678
  var ACTIVE_JOB_SLEEP_MS = 1e3;
24253
24679
  var NO_JOBS_SLEEP_MS = 5e3;
24254
24680
  var POST_JOB_SLEEP_MS = 3e3;
24681
+ var runPendingJob = async (job, logPrefix) => {
24682
+ const redactAgentData = (payload) => payload?.agentData ? { ...payload, agentData: Object.keys(payload.agentData) } : payload;
24683
+ logger_1.logger.info("Job details", {
24684
+ job: {
24685
+ ...job,
24686
+ tasks: job.tasks?.map((task) => ({
24687
+ ...task,
24688
+ input: redactAgentData(task.input),
24689
+ payload: redactAgentData(task.payload)
24690
+ }))
24691
+ }
24692
+ });
24693
+ if (!job.id) {
24694
+ throw new ditto_codegen_types_1.MissingJobIdError();
24695
+ }
24696
+ const jobId = job.id;
24697
+ logger_1.logger.info(`${logPrefix} Selected job`, { jobId });
24698
+ const freshToken = await codeGenerationService_1.codeGenerationService.refreshAccessToken();
24699
+ (0, config_1.setCurrentAccessToken)(freshToken);
24700
+ await codeGenerationService_1.codeGenerationService.markJobAsRuninng(jobId);
24701
+ await processJob(job);
24702
+ await codeGenerationService_1.codeGenerationService.markJobAsCompleted(jobId);
24703
+ logger_1.logger.info(`${logPrefix} Processed job`, { jobId });
24704
+ };
24255
24705
  var alwaysOnLoop = async () => {
24256
24706
  while (true) {
24257
24707
  try {
24258
24708
  if (context_1.ctx.hasActiveJobs()) {
24259
24709
  console.log(`[Loop] Local active job in progress. Sleeping ${ACTIVE_JOB_SLEEP_MS}ms`);
24260
- await delay(ACTIVE_JOB_SLEEP_MS);
24710
+ await (0, utils_1.delay)(ACTIVE_JOB_SLEEP_MS);
24261
24711
  continue;
24262
24712
  }
24263
24713
  const jobs = await codeGenerationService_1.codeGenerationService.fetchPendingJobs();
24264
24714
  console.log("[Loop] Fetched pending jobs", { count: jobs.length });
24265
24715
  if (jobs.length === 0) {
24266
24716
  console.log("[Loop] No pending jobs, sleeping");
24267
- await delay(NO_JOBS_SLEEP_MS);
24717
+ await (0, utils_1.delay)(NO_JOBS_SLEEP_MS);
24268
24718
  continue;
24269
24719
  }
24270
- const job = jobs[0];
24271
- const redactAgentData = (payload) => payload?.agentData ? { ...payload, agentData: Object.keys(payload.agentData) } : payload;
24272
- logger_1.logger.info("Job details", {
24273
- job: {
24274
- ...job,
24275
- tasks: job.tasks?.map((task) => ({
24276
- ...task,
24277
- input: redactAgentData(task.input),
24278
- payload: redactAgentData(task.payload)
24279
- }))
24280
- }
24281
- });
24282
- if (!job.id) {
24283
- throw new ditto_codegen_types_1.MissingJobIdError();
24284
- }
24285
- const jobId = job.id;
24286
- logger_1.logger.info("[Loop] Selected job", { jobId });
24287
- const freshToken = await codeGenerationService_1.codeGenerationService.refreshAccessToken();
24288
- (0, config_1.setCurrentAccessToken)(freshToken);
24289
- await codeGenerationService_1.codeGenerationService.markJobAsRuninng(jobId);
24290
- await processJob(job);
24291
- await codeGenerationService_1.codeGenerationService.markJobAsCompleted(jobId);
24292
- logger_1.logger.info("[Loop] Processed job", { jobId });
24293
- await delay(POST_JOB_SLEEP_MS);
24720
+ await runPendingJob(jobs[0], POLL_LOG_PREFIX);
24721
+ await (0, utils_1.delay)(POST_JOB_SLEEP_MS);
24294
24722
  } catch (loopError) {
24295
24723
  logger_1.logger.error("Polling loop encountered an error. Will continue after backoff:", {
24296
24724
  error: loopError instanceof Error ? loopError.message : String(loopError)
24297
24725
  });
24298
- await delay(NO_JOBS_SLEEP_MS);
24726
+ await (0, utils_1.delay)(NO_JOBS_SLEEP_MS);
24299
24727
  }
24300
24728
  context_1.ctx.resetJobs();
24301
24729
  }
@@ -24333,7 +24761,13 @@ async function main(ctx) {
24333
24761
  metasiteId: ctx.metasiteId
24334
24762
  });
24335
24763
  await initOpenCode();
24336
- await alwaysOnLoop();
24764
+ const nudgeListenerServer = new nudge_listener_server_1.NudgeListenerServer({
24765
+ fetchPendingJobs: () => codeGenerationService_1.codeGenerationService.fetchPendingJobs(),
24766
+ findJob: (jobId) => codeGenerationService_1.codeGenerationService.findJob(jobId),
24767
+ canRunJob: (job) => codeGenerationService_1.codeGenerationService.shouldProcessJob(job),
24768
+ runJob: (job) => runPendingJob(job, PUSH_LOG_PREFIX)
24769
+ }, (0, job_fetching_1.getJobFetchPort)());
24770
+ await (0, job_fetching_1.startJobFetching)({ runPollLoop: alwaysOnLoop, nudgeListenerServer });
24337
24771
  }
24338
24772
  var getFirstTask = (job) => {
24339
24773
  return job.tasks?.[0];
@@ -24377,7 +24811,7 @@ async function reportPreDispatchFailure(jobId, error) {
24377
24811
  });
24378
24812
  try {
24379
24813
  await codeGenerationService_1.codeGenerationService.markJobAsFailed(jobId, codegenError);
24380
- await (0, utils_1.updateAllRunningTasks)(jobId, CodeGenService_1.Status.FAILED, codegenError);
24814
+ await (0, utils_2.updateAllRunningTasks)(jobId, CodeGenService_1.Status.FAILED, codegenError);
24381
24815
  } catch (reportError) {
24382
24816
  logger_1.logger.error("[Job] Could not mark the job failed", {
24383
24817
  jobId,
@@ -24444,6 +24878,7 @@ async function processJob(job) {
24444
24878
  } finally {
24445
24879
  jobCancellationMonitor.stop();
24446
24880
  jobTimeoutMonitor.stop();
24881
+ context_1.ctx.resetJobs();
24447
24882
  }
24448
24883
  });
24449
24884
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/ditto-codegen-public",
3
- "version": "1.0.441",
3
+ "version": "1.0.443",
4
4
  "description": "AI-powered Wix CLI app generator - standalone executable",
5
5
  "scripts": {
6
6
  "build": "node build.mjs",
@@ -29,5 +29,5 @@
29
29
  "esbuild": "^0.27.2",
30
30
  "vitest": "^4.0.16"
31
31
  },
32
- "falconPackageHash": "b04a4f2857569aa9b70fbf2fc57a6b3c7db84e442334799481a9cc6c"
32
+ "falconPackageHash": "da26fbed4274b90d804fbf1672491b33c4725d7a18de167c461deb2f"
33
33
  }