@squidcloud/cli 1.0.449 → 1.0.451

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.
package/dist/index.js CHANGED
@@ -11221,6 +11221,43 @@ const packageJson = __webpack_require__(2108)
11221
11221
 
11222
11222
  const version = packageJson.version
11223
11223
 
11224
+ // Array of tips to display randomly
11225
+ const TIPS = [
11226
+ '🔐 encrypt with Dotenvx: https://dotenvx.com',
11227
+ '🔐 prevent committing .env to code: https://dotenvx.com/precommit',
11228
+ '🔐 prevent building .env in docker: https://dotenvx.com/prebuild',
11229
+ '🤖 agentic secret storage: https://dotenvx.com/as2',
11230
+ '⚡️ secrets for agents: https://dotenvx.com/as2',
11231
+ '🛡️ auth for agents: https://vestauth.com',
11232
+ '🛠️ run anywhere with `dotenvx run -- yourcommand`',
11233
+ '⚙️ specify custom .env file path with { path: \'/custom/path/.env\' }',
11234
+ '⚙️ enable debug logging with { debug: true }',
11235
+ '⚙️ override existing env vars with { override: true }',
11236
+ '⚙️ suppress all logs with { quiet: true }',
11237
+ '⚙️ write to custom object with { processEnv: myObject }',
11238
+ '⚙️ load multiple .env files with { path: [\'.env.local\', \'.env\'] }'
11239
+ ]
11240
+
11241
+ // Get a random tip from the tips array
11242
+ function _getRandomTip () {
11243
+ return TIPS[Math.floor(Math.random() * TIPS.length)]
11244
+ }
11245
+
11246
+ function parseBoolean (value) {
11247
+ if (typeof value === 'string') {
11248
+ return !['false', '0', 'no', 'off', ''].includes(value.toLowerCase())
11249
+ }
11250
+ return Boolean(value)
11251
+ }
11252
+
11253
+ function supportsAnsi () {
11254
+ return process.stdout.isTTY // && process.env.TERM !== 'dumb'
11255
+ }
11256
+
11257
+ function dim (text) {
11258
+ return supportsAnsi() ? `\x1b[2m${text}\x1b[0m` : text
11259
+ }
11260
+
11224
11261
  const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
11225
11262
 
11226
11263
  // Parse src into an Object
@@ -11306,7 +11343,7 @@ function _parseVault (options) {
11306
11343
  }
11307
11344
 
11308
11345
  function _warn (message) {
11309
- console.log(`[dotenv@${version}][WARN] ${message}`)
11346
+ console.error(`[dotenv@${version}][WARN] ${message}`)
11310
11347
  }
11311
11348
 
11312
11349
  function _debug (message) {
@@ -11404,8 +11441,8 @@ function _resolveHome (envPath) {
11404
11441
  }
11405
11442
 
11406
11443
  function _configVault (options) {
11407
- const debug = Boolean(options && options.debug)
11408
- const quiet = options && 'quiet' in options ? options.quiet : true
11444
+ const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || (options && options.debug))
11445
+ const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || (options && options.quiet))
11409
11446
 
11410
11447
  if (debug || !quiet) {
11411
11448
  _log('Loading env from encrypted .env.vault')
@@ -11426,8 +11463,12 @@ function _configVault (options) {
11426
11463
  function configDotenv (options) {
11427
11464
  const dotenvPath = path.resolve(process.cwd(), '.env')
11428
11465
  let encoding = 'utf8'
11429
- const debug = Boolean(options && options.debug)
11430
- const quiet = options && 'quiet' in options ? options.quiet : true
11466
+ let processEnv = process.env
11467
+ if (options && options.processEnv != null) {
11468
+ processEnv = options.processEnv
11469
+ }
11470
+ let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || (options && options.debug))
11471
+ let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || (options && options.quiet))
11431
11472
 
11432
11473
  if (options && options.encoding) {
11433
11474
  encoding = options.encoding
@@ -11467,15 +11508,14 @@ function configDotenv (options) {
11467
11508
  }
11468
11509
  }
11469
11510
 
11470
- let processEnv = process.env
11471
- if (options && options.processEnv != null) {
11472
- processEnv = options.processEnv
11473
- }
11511
+ const populated = DotenvModule.populate(processEnv, parsedAll, options)
11474
11512
 
11475
- DotenvModule.populate(processEnv, parsedAll, options)
11513
+ // handle user settings DOTENV_CONFIG_ options inside .env file(s)
11514
+ debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug)
11515
+ quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet)
11476
11516
 
11477
11517
  if (debug || !quiet) {
11478
- const keysCount = Object.keys(parsedAll).length
11518
+ const keysCount = Object.keys(populated).length
11479
11519
  const shortPaths = []
11480
11520
  for (const filePath of optionPaths) {
11481
11521
  try {
@@ -11489,7 +11529,7 @@ function configDotenv (options) {
11489
11529
  }
11490
11530
  }
11491
11531
 
11492
- _log(`injecting env (${keysCount}) from ${shortPaths.join(',')}`)
11532
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(',')} ${dim(`-- tip: ${_getRandomTip()}`)}`)
11493
11533
  }
11494
11534
 
11495
11535
  if (lastError) {
@@ -11553,6 +11593,7 @@ function decrypt (encrypted, keyStr) {
11553
11593
  function populate (processEnv, parsed, options = {}) {
11554
11594
  const debug = Boolean(options && options.debug)
11555
11595
  const override = Boolean(options && options.override)
11596
+ const populated = {}
11556
11597
 
11557
11598
  if (typeof parsed !== 'object') {
11558
11599
  const err = new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate')
@@ -11565,6 +11606,7 @@ function populate (processEnv, parsed, options = {}) {
11565
11606
  if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
11566
11607
  if (override === true) {
11567
11608
  processEnv[key] = parsed[key]
11609
+ populated[key] = parsed[key]
11568
11610
  }
11569
11611
 
11570
11612
  if (debug) {
@@ -11576,8 +11618,11 @@ function populate (processEnv, parsed, options = {}) {
11576
11618
  }
11577
11619
  } else {
11578
11620
  processEnv[key] = parsed[key]
11621
+ populated[key] = parsed[key]
11579
11622
  }
11580
11623
  }
11624
+
11625
+ return populated
11581
11626
  }
11582
11627
 
11583
11628
  const DotenvModule = {
@@ -29656,6 +29701,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
29656
29701
  exports.OPEN_AI_CREATE_SPEECH_FORMATS = exports.AI_AUDIO_CREATE_SPEECH_MODEL_NAMES = exports.AI_AUDIO_TRANSCRIPTION_MODEL_NAMES = exports.AI_IMAGE_MODEL_NAMES = exports.FLUX_MODEL_NAMES = exports.STABLE_DIFFUSION_MODEL_NAMES = exports.OPENAI_AUDIO_MODEL_NAMES = exports.OPENAI_AUDIO_CREATE_SPEECH_MODEL_NAMES = exports.OPENAI_AUDIO_TRANSCRIPTION_MODEL_NAMES = exports.OPENAI_IMAGE_MODEL_NAMES = exports.AI_EMBEDDINGS_MODEL_NAMES = exports.BEDROCK_EMBEDDING_MODEL_NAMES = exports.VOYAGE_EMBEDDING_MODEL_NAMES = exports.OPENAI_EMBEDDINGS_MODEL_NAMES = exports.VENDOR_AI_CHAT_MODEL_NAMES = exports.ANTHROPIC_CHAT_MODEL_NAMES = exports.GROK_CHAT_MODEL_NAMES = exports.GEMINI_CHAT_MODEL_NAMES = exports.OPENAI_CHAT_MODEL_NAMES = exports.AI_PROVIDER_TYPES = exports.RERANK_PROVIDERS = void 0;
29657
29702
  exports.isVendorAiChatModelName = isVendorAiChatModelName;
29658
29703
  exports.isAiEmbeddingsModelName = isAiEmbeddingsModelName;
29704
+ exports.isIntegrationEmbeddingModelSpec = isIntegrationEmbeddingModelSpec;
29659
29705
  exports.isIntegrationModelSpec = isIntegrationModelSpec;
29660
29706
  /**
29661
29707
  * @category AI
@@ -29681,7 +29727,7 @@ exports.AI_PROVIDER_TYPES = [
29681
29727
  * Public OpenAI chat model names (active models only).
29682
29728
  * @category AI
29683
29729
  */
29684
- exports.OPENAI_CHAT_MODEL_NAMES = ['gpt-5-mini', 'gpt-5-nano', 'gpt-5.2', 'gpt-5.2-pro'];
29730
+ exports.OPENAI_CHAT_MODEL_NAMES = ['gpt-5-mini', 'gpt-5-nano', 'gpt-5.2', 'gpt-5.2-pro', 'gpt-5.4'];
29685
29731
  /**
29686
29732
  * Public Gemini chat model names (active models only).
29687
29733
  * @category AI
@@ -29742,6 +29788,13 @@ exports.AI_EMBEDDINGS_MODEL_NAMES = [
29742
29788
  function isAiEmbeddingsModelName(modelName) {
29743
29789
  return exports.AI_EMBEDDINGS_MODEL_NAMES.includes(modelName);
29744
29790
  }
29791
+ /**
29792
+ * Type guard for `IntegrationEmbeddingModelSpec`.
29793
+ * @category AI
29794
+ */
29795
+ function isIntegrationEmbeddingModelSpec(model) {
29796
+ return (typeof model === 'object' && model !== null && 'integrationId' in model && 'model' in model && 'dimensions' in model);
29797
+ }
29745
29798
  /**
29746
29799
  * The supported AI image generation model names.
29747
29800
  * @category AI
@@ -29893,6 +29946,7 @@ exports.INTEGRATION_TYPES = [
29893
29946
  'legend',
29894
29947
  'teams',
29895
29948
  'openai_compatible',
29949
+ 'openai_compatible_embedding',
29896
29950
  ];
29897
29951
  /**
29898
29952
  * @category Database
@@ -30158,6 +30212,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
30158
30212
  exports.extractFromBuffer = extractFromBuffer;
30159
30213
  exports.checkFileOrDirExists = checkFileOrDirExists;
30160
30214
  exports.assertFileOrDirExists = assertFileOrDirExists;
30215
+ exports.getRuntimeVmLabel = getRuntimeVmLabel;
30161
30216
  const assertic_1 = __webpack_require__(3205);
30162
30217
  const decompress_1 = __importDefault(__webpack_require__(482));
30163
30218
  const fs = __importStar(__webpack_require__(9896));
@@ -30178,6 +30233,14 @@ async function assertFileOrDirExists(path, errorProvider) {
30178
30233
  const exists = await checkFileOrDirExists(path);
30179
30234
  (0, assertic_1.assertTruthy)(exists, errorProvider || `File not exists: ${path}`);
30180
30235
  }
30236
+ function getRuntimeVmLabel() {
30237
+ // @ts-expect-error Bun is not defined in Node.js types
30238
+ if (typeof Bun !== 'undefined') {
30239
+ // @ts-expect-error Bun is not defined in Node.js types
30240
+ return `Bun v${Bun.version}`;
30241
+ }
30242
+ return `Node ${process.version}`;
30243
+ }
30181
30244
 
30182
30245
 
30183
30246
  /***/ },
@@ -30325,7 +30388,11 @@ function isSquidDebugEnabledByDefault() {
30325
30388
  }
30326
30389
  return param === '1' || param === 'true';
30327
30390
  }
30328
- enableDebugLogs(isSquidDebugEnabledByDefault());
30391
+ // Auto-initialize debug flag only if not explicitly set already.
30392
+ // This prevents bundled code from overriding the flag set by the host application.
30393
+ if (getGlobal()?.['SQUID_LOG_DEBUG_ENABLED'] === undefined) {
30394
+ enableDebugLogs(isSquidDebugEnabledByDefault());
30395
+ }
30329
30396
  /** @internal */
30330
30397
  function disableTimestampsInLog() {
30331
30398
  const globalObj = getGlobal();
@@ -30387,14 +30454,12 @@ exports.KOTLIN_CONTROLLERS = [
30387
30454
  'mutation',
30388
30455
  'native-query',
30389
30456
  'observability',
30390
- 'openapi',
30391
30457
  'query',
30392
30458
  'queue',
30393
30459
  'quota',
30394
30460
  'scheduler',
30395
30461
  'secret',
30396
30462
  'storage',
30397
- 'webhooks',
30398
30463
  'ws',
30399
30464
  'internal-extraction',
30400
30465
  'notification',
@@ -30551,6 +30616,7 @@ async function getOpenApiSpecAndControllersFromFile(codeDir) {
30551
30616
  */
30552
30617
  async function populateOpenApiControllersMap(bundleData, codeDir, codeType) {
30553
30618
  const openApiControllersMap = {};
30619
+ const openApiSpecsMap = {};
30554
30620
  const openApiSpecAndControllers = await getOpenApiSpecAndControllersFromFile(codeDir);
30555
30621
  for (const [key, value] of Object.entries(openApiSpecAndControllers.openApiControllersMap)) {
30556
30622
  if (key === 'default' && codeType === 'connector') {
@@ -30559,7 +30625,14 @@ async function populateOpenApiControllersMap(bundleData, codeDir, codeType) {
30559
30625
  }
30560
30626
  openApiControllersMap[key] = JSON.parse(value);
30561
30627
  }
30628
+ for (const [key, value] of Object.entries(openApiSpecAndControllers.openApiSpecsMap)) {
30629
+ if (key === 'default' && codeType === 'connector') {
30630
+ continue;
30631
+ }
30632
+ openApiSpecsMap[key] = value;
30633
+ }
30562
30634
  bundleData.openApiControllersMap = openApiControllersMap;
30635
+ bundleData.openApiSpecsMap = openApiSpecsMap;
30563
30636
  }
30564
30637
 
30565
30638
 
@@ -30866,10 +30939,15 @@ const resolve_1 = __webpack_require__(412);
30866
30939
  const shell_runner_1 = __webpack_require__(3089);
30867
30940
  class TsoaUtils {
30868
30941
  constructor() { }
30869
- static async generateAllSpecs(verbose = false, supportsDefault = true) {
30942
+ /**
30943
+ * @param projectDir - Root directory of the user's backend project.
30944
+ * @param supportsDefault - Include the default tsoa spec (pass false for connectors).
30945
+ * @param verbose - Log detailed progress and diagnostic messages.
30946
+ */
30947
+ static async generateAllSpecs(projectDir, supportsDefault, verbose) {
30870
30948
  const specsMap = {};
30871
30949
  // Read files in the current directory.
30872
- let files = await fsPromises.readdir(process.cwd());
30950
+ let files = await fsPromises.readdir(projectDir);
30873
30951
  // Filter files matching the TSOA pattern (e.g. "tsoa.json", "tsoa-myapp.json", "tsoaAnother.json").
30874
30952
  files = files.filter(file => /^tsoa.*\.json$/.test(file));
30875
30953
  // If an exact "tsoa.json" is not present, get the fallback path.
@@ -30877,7 +30955,7 @@ class TsoaUtils {
30877
30955
  const hasAnyTsoaFilesProvidedByUser = files.length > 0;
30878
30956
  const localBackendModule = await (0, resolve_1.findModulePath)('@squidcloud/local-backend');
30879
30957
  if (localBackendModule) {
30880
- const fallback = path.relative(process.cwd(), path.resolve(localBackendModule, '../../tsoa.json'));
30958
+ const fallback = path.relative(projectDir, path.resolve(localBackendModule, '../../tsoa.json'));
30881
30959
  if (verbose) {
30882
30960
  console.log(`"tsoa.json" not found in current directory. Using fallback: ${fallback}`);
30883
30961
  }
@@ -30888,7 +30966,7 @@ class TsoaUtils {
30888
30966
  // If this directory contains no '@Route' annotations - there are no tsoa controllers and there is no need to
30889
30967
  // run tsoa at all.
30890
30968
  if (!hasAnyTsoaFilesProvidedByUser) {
30891
- const hasTsoaAnnotationsInUserCode = await checkUserCodeUsesTsoa(path.resolve(process.cwd(), 'src'));
30969
+ const hasTsoaAnnotationsInUserCode = await checkUserCodeUsesTsoa(path.resolve(projectDir, 'src'));
30892
30970
  if (!hasTsoaAnnotationsInUserCode) {
30893
30971
  if (verbose) {
30894
30972
  console.log('No TSOA annotations were found in the user code; skipping API generation with TSOA.');
@@ -30915,14 +30993,16 @@ class TsoaUtils {
30915
30993
  console.log(`Processing config file: ${file}`);
30916
30994
  }
30917
30995
  // Use native join to create the folder.
30918
- const outputDir = path.join('dist', specName);
30919
- if (!fs.existsSync(outputDir)) {
30920
- fs.mkdirSync(outputDir, { recursive: true });
30996
+ // absOutputDir is used for Node fs operations; relOutputDir is passed to tsoa (which runs in projectDir).
30997
+ const relOutputDir = path.join('dist', specName);
30998
+ const absOutputDir = path.resolve(projectDir, relOutputDir);
30999
+ if (!fs.existsSync(absOutputDir)) {
31000
+ fs.mkdirSync(absOutputDir, { recursive: true });
30921
31001
  }
30922
31002
  // Read the original TSOA config.
30923
31003
  let configContent;
30924
31004
  try {
30925
- configContent = await fsPromises.readFile(file, { encoding: 'utf8' });
31005
+ configContent = await fsPromises.readFile(path.resolve(projectDir, file), { encoding: 'utf8' });
30926
31006
  }
30927
31007
  catch (e) {
30928
31008
  if (verbose) {
@@ -30941,10 +31021,11 @@ class TsoaUtils {
30941
31021
  throw e;
30942
31022
  }
30943
31023
  // Override the output directories for both spec and routes.
31024
+ // Use relative paths so tsoa (running in projectDir) resolves them correctly.
30944
31025
  config.spec = config.spec || {};
30945
31026
  config.routes = config.routes || {};
30946
- config.spec.outputDirectory = outputDir;
30947
- config.routes.routesDir = outputDir;
31027
+ config.spec.outputDirectory = relOutputDir;
31028
+ config.routes.routesDir = relOutputDir;
30948
31029
  // Fix middlewareTemplate path when using fallback from local-backend
30949
31030
  if (config.routes.middlewareTemplate && !path.isAbsolute(file)) {
30950
31031
  const localBackendModule = await (0, resolve_1.findModulePath)('@squidcloud/local-backend');
@@ -30966,7 +31047,7 @@ class TsoaUtils {
30966
31047
  // Rewrite the middlewareTemplate path to be relative to the current working directory.
30967
31048
  const templatePath = path.resolve(packageRoot, 'dist/local-backend/openapi-template.hbs');
30968
31049
  // Calculate relative path from current working directory (which might be backend/ or test-tsoa/backend/)
30969
- config.routes.middlewareTemplate = path.relative(process.cwd(), templatePath);
31050
+ config.routes.middlewareTemplate = path.relative(projectDir, templatePath);
30970
31051
  if (verbose) {
30971
31052
  console.log(`Rewritten middlewareTemplate path to: ${config.routes.middlewareTemplate}`);
30972
31053
  console.log(`Template absolute path: ${templatePath}`);
@@ -30975,27 +31056,28 @@ class TsoaUtils {
30975
31056
  }
30976
31057
  }
30977
31058
  }
30978
- // Use a relative path for the temporary config file.
30979
- const tempConfigPath = `temp-tsoa-config-${specName}.json`;
31059
+ // Use a relative name for the tsoa command (tsoa runs in projectDir) and absolute path for Node fs operations.
31060
+ const tempConfigName = `temp-tsoa-config-${specName}.json`;
31061
+ const absTempConfigPath = path.resolve(projectDir, tempConfigName);
30980
31062
  try {
30981
- await fsPromises.writeFile(tempConfigPath, JSON.stringify(config, null, 2), { encoding: 'utf8' });
31063
+ await fsPromises.writeFile(absTempConfigPath, JSON.stringify(config, null, 2), { encoding: 'utf8' });
30982
31064
  }
30983
31065
  catch (e) {
30984
31066
  if (verbose) {
30985
- console.error(`Error writing temporary config file ${tempConfigPath}:`, e);
31067
+ console.error(`Error writing temporary config file ${absTempConfigPath}:`, e);
30986
31068
  }
30987
31069
  throw e;
30988
31070
  }
30989
31071
  // Build the TSOA commands (without using -o) that reference the temporary config.
30990
- const specCmd = `npx tsoa spec -c ${tempConfigPath}`;
30991
- const routesCmd = `npx tsoa routes -c ${tempConfigPath}`;
31072
+ const specCmd = `npx tsoa spec -c ${tempConfigName}`;
31073
+ const routesCmd = `npx tsoa routes -c ${tempConfigName}`;
30992
31074
  if (verbose) {
30993
- console.log(`Processing ${file} as "${specName}" with temporary config ${tempConfigPath}`);
31075
+ console.log(`Processing ${file} as "${specName}" with temporary config ${tempConfigName}`);
30994
31076
  console.log(`Running: ${specCmd}`);
30995
31077
  console.log(`Running: ${routesCmd}`);
30996
31078
  }
30997
31079
  try {
30998
- await Promise.all([(0, shell_runner_1.runInShell)(specCmd, false, process.cwd()), (0, shell_runner_1.runInShell)(routesCmd, false, process.cwd())]);
31080
+ await Promise.all([(0, shell_runner_1.runInShell)(specCmd, false, projectDir), (0, shell_runner_1.runInShell)(routesCmd, false, projectDir)]);
30999
31081
  }
31000
31082
  catch (e) {
31001
31083
  const combinedOutput = e?.combinedOutput;
@@ -31005,18 +31087,18 @@ class TsoaUtils {
31005
31087
  console.log(`No controllers found for ${file}, skipping...`);
31006
31088
  }
31007
31089
  // Clean up the temporary config file before continuing.
31008
- await fsPromises.unlink(tempConfigPath);
31090
+ await fsPromises.unlink(absTempConfigPath);
31009
31091
  continue;
31010
31092
  }
31011
31093
  if (verbose) {
31012
31094
  console.error(`Error processing ${file}:\n`, errorMessage);
31013
31095
  }
31014
- await fsPromises.unlink(tempConfigPath);
31096
+ await fsPromises.unlink(absTempConfigPath);
31015
31097
  throw new Error(`Failed to process ${file}: ` + errorMessage);
31016
31098
  }
31017
- // Define output file paths.
31018
- const swaggerOutput = path.join(outputDir, 'swagger.json');
31019
- const routesOutput = path.join(outputDir, 'routes.ts');
31099
+ // Define output file paths using absolute paths for Node fs operations.
31100
+ const swaggerOutput = path.join(absOutputDir, 'swagger.json');
31101
+ const routesOutput = path.join(absOutputDir, 'routes.ts');
31020
31102
  try {
31021
31103
  const swaggerContent = await fsPromises.readFile(swaggerOutput, { encoding: 'utf8' });
31022
31104
  const modifiedSwaggerContent = await TsoaUtils.attachCodeSamples(swaggerContent);
@@ -31030,11 +31112,11 @@ class TsoaUtils {
31030
31112
  if (verbose) {
31031
31113
  console.error(`Error reading generated files for ${file}:`, e);
31032
31114
  }
31033
- await fsPromises.unlink(tempConfigPath);
31115
+ await fsPromises.unlink(absTempConfigPath);
31034
31116
  throw e;
31035
31117
  }
31036
31118
  // Clean up the temporary config file.
31037
- await fsPromises.unlink(tempConfigPath);
31119
+ await fsPromises.unlink(absTempConfigPath);
31038
31120
  }
31039
31121
  const openApiControllersMap = {};
31040
31122
  const openApiSpecsMap = {};
@@ -31236,6 +31318,7 @@ const process_env_utils_1 = __webpack_require__(9360);
31236
31318
  const resolve_1 = __webpack_require__(412);
31237
31319
  const tsoa_utils_1 = __webpack_require__(1431);
31238
31320
  const packageJson = __importStar(__webpack_require__(8330));
31321
+ const update_skills_1 = __webpack_require__(7286);
31239
31322
  const logging_1 = __webpack_require__(443);
31240
31323
  const process_utils_1 = __webpack_require__(8251);
31241
31324
  const resolve_2 = __webpack_require__(3878);
@@ -31247,21 +31330,38 @@ const version_check_1 = __webpack_require__(4827);
31247
31330
  // - Default: colors only if TTY (stdout is a terminal).
31248
31331
  const isTerminal = process.stdout.isTTY;
31249
31332
  const useColorsInOutput = (0, process_env_utils_1.isEnvVarTruthy)('FORCE_COLOR') || (!(0, process_env_utils_1.isEnvVarTruthy)('NO_COLOR') && isTerminal);
31250
- async function displayVersionWarning(versionCheckPromise) {
31251
- try {
31252
- const versionWarning = await versionCheckPromise;
31253
- if (versionWarning) {
31254
- console.warn(versionWarning);
31333
+ /**
31334
+ * Add Python source files and requirements.txt to a zip archive.
31335
+ * Used by both Python-only and hybrid builds.
31336
+ */
31337
+ function addPythonFilesToBundle(zip) {
31338
+ const SKIP_DIRS = new Set(['__pycache__', '__python_deps__']);
31339
+ function addPyFiles(dirPath, zipDir) {
31340
+ for (const entry of fsSync.readdirSync(dirPath, { withFileTypes: true })) {
31341
+ if (entry.name.startsWith('.'))
31342
+ continue;
31343
+ const fullPath = path_1.default.join(dirPath, entry.name);
31344
+ if (entry.isDirectory()) {
31345
+ if (SKIP_DIRS.has(entry.name))
31346
+ continue;
31347
+ addPyFiles(fullPath, path_1.default.join(zipDir, entry.name));
31348
+ }
31349
+ else if (entry.isFile() && !entry.name.endsWith('.pyc')) {
31350
+ zip.addLocalFile(fullPath, zipDir || undefined);
31351
+ }
31255
31352
  }
31256
31353
  }
31257
- catch (_ignored) { }
31354
+ addPyFiles('src', '');
31355
+ if (fsSync.existsSync('requirements.txt')) {
31356
+ zip.addLocalFile('requirements.txt');
31357
+ }
31258
31358
  }
31259
31359
  async function build({ verbose, dev, skipVersionCheck }) {
31260
31360
  const buildPhaseStart = Date.now();
31261
31361
  (0, global_utils_1.enableDebugLogs)(verbose || (0, global_utils_1.isDebugEnabled)());
31262
31362
  (0, logpipes_1.installConsoleOverrides)(enable_debug_decorator_utils_1.debugLogFilterPipe);
31263
31363
  console.debug(`Starting Squid project build. CLI package version: ${packageJson.version}`);
31264
- await (0, validate_1.validateSquidProject)();
31364
+ const projectInfo = await (0, validate_1.validateSquidProject)();
31265
31365
  // Start version checks in the background if not disabled.
31266
31366
  const shouldSkipVersionCheck = skipVersionCheck || (0, process_env_utils_1.isEnvVarTruthy)('SQUID_SKIP_BUILD_TIME_VERSION_CHECK');
31267
31367
  // SQUID_CURRENT_CLI_VERSION_OVERRIDE is used for testing.
@@ -31273,10 +31373,24 @@ async function build({ verbose, dev, skipVersionCheck }) {
31273
31373
  await fs_1.promises.rm(distPath, { recursive: true, force: true });
31274
31374
  }
31275
31375
  await fs_1.promises.mkdir(distPath);
31376
+ // Python-only projects: zip src/ + requirements.txt, no webpack.
31377
+ if (projectInfo.isPython && !projectInfo.isHybrid) {
31378
+ console.log((0, logging_1.primary)('Building Python bundle...'));
31379
+ const bundlePath = path_1.default.join(distPath, 'bundle.zip');
31380
+ const zip = new adm_zip_1.default();
31381
+ addPythonFilesToBundle(zip);
31382
+ zip.writeZip(bundlePath);
31383
+ const stats = fsSync.statSync(bundlePath);
31384
+ console.log(`Python bundle created: ${bundlePath} (${(stats.size / 1024).toFixed(1)} KB)`);
31385
+ await (0, version_check_1.displayVersionWarningIfOutdated)(versionCheckPromise);
31386
+ (0, update_skills_1.displaySkillsWarningIfOutdated)();
31387
+ console.debug((0, log_utils_1.timeSince)(buildPhaseStart, 'Build time '));
31388
+ return;
31389
+ }
31276
31390
  const isSquidConnector = (0, process_env_utils_1.isEnvVarTruthy)('SQUID_CONNECTOR');
31277
31391
  const specsStart = Date.now();
31278
- const openApiSpecAndControllers = await tsoa_utils_1.TsoaUtils.generateAllSpecs(verbose, !isSquidConnector);
31279
- console.debug((0, log_utils_1.timeSince)(specsStart, ' TsoaUtils.generateAllSpecs '));
31392
+ const openApiSpecAndControllers = await tsoa_utils_1.TsoaUtils.generateAllSpecs(process.cwd(), !isSquidConnector, verbose);
31393
+ console.debug((0, log_utils_1.timeSince)(specsStart, 'OpenAPI specs generation time '));
31280
31394
  await fs_1.promises.writeFile(path_1.default.join(distPath, '', 'openapi-spec-and-controllers.json'), JSON.stringify(openApiSpecAndControllers));
31281
31395
  const isUserConfigMode = fsSync.existsSync(resolve_2.USER_WEBPACK_CONFIG_PATH);
31282
31396
  const webpackConfigPath = isUserConfigMode ? resolve_2.USER_WEBPACK_CONFIG_PATH : resolve_2.BUILT_IN_WEBPACK_CONFIG_PATH;
@@ -31291,7 +31405,7 @@ async function build({ verbose, dev, skipVersionCheck }) {
31291
31405
  await new Promise((resolve, reject) => {
31292
31406
  (0, webpack_1.default)(webpackConfig, (err, stats) => {
31293
31407
  if (err) {
31294
- console.debug((0, log_utils_1.timeSince)(webpackStart, ' webpack build '));
31408
+ console.debug((0, log_utils_1.timeSince)(webpackStart, 'Webpack build time '));
31295
31409
  console.error('Build failed a fatal error.');
31296
31410
  return reject(err);
31297
31411
  }
@@ -31308,34 +31422,47 @@ async function build({ verbose, dev, skipVersionCheck }) {
31308
31422
  }));
31309
31423
  }
31310
31424
  if (stats?.hasErrors()) {
31311
- console.debug((0, log_utils_1.timeSince)(webpackStart, ' webpack build '));
31425
+ console.debug((0, log_utils_1.timeSince)(webpackStart, 'Webpack build time '));
31312
31426
  return reject(new Error('Build failed with errors.'));
31313
31427
  }
31314
- console.debug((0, log_utils_1.timeSince)(webpackStart, ' webpack build '));
31428
+ console.debug((0, log_utils_1.timeSince)(webpackStart, 'Webpack build time '));
31315
31429
  console.log('Build succeeded.');
31316
31430
  resolve();
31317
31431
  });
31318
31432
  });
31433
+ // For hybrid projects, add Python files to the webpack-produced bundle.
31434
+ if (projectInfo.isHybrid) {
31435
+ console.log('Adding Python files to hybrid bundle...');
31436
+ const zipPath = path_1.default.join(distPath, 'bundle.zip');
31437
+ const zip = new adm_zip_1.default(zipPath);
31438
+ addPythonFilesToBundle(zip);
31439
+ zip.writeZip(zipPath);
31440
+ console.log('Python files added to bundle.');
31441
+ }
31319
31442
  // Generate connector metadata if this is a connector build.
31320
31443
  if (isSquidConnector) {
31321
31444
  console.debug('Generating connector metadata...');
31322
31445
  const metadataStart = Date.now();
31323
31446
  await generateConnectorMetadata(distPath);
31324
- console.debug((0, log_utils_1.timeSince)(metadataStart, ' generateConnectorMetadata '));
31447
+ console.debug((0, log_utils_1.timeSince)(metadataStart, 'Connector metadata generation time '));
31325
31448
  // Add metadata to bundle.zip.
31449
+ const metadataBundleStart = Date.now();
31326
31450
  const zipPath = path_1.default.join(distPath, 'bundle.zip');
31327
31451
  const zip = new adm_zip_1.default(zipPath);
31328
31452
  zip.addLocalFile(path_1.default.join(distPath, connector_types_1.CONNECTOR_METADATA_JSON_FILE));
31329
31453
  zip.writeZip(zipPath);
31330
31454
  console.debug('Added connector metadata to bundle.zip');
31455
+ console.debug((0, log_utils_1.timeSince)(metadataBundleStart, 'Connector metadata bundling time '));
31331
31456
  }
31332
31457
  // Show version check warning after successful build.
31333
- await displayVersionWarning(versionCheckPromise);
31334
- console.debug((0, log_utils_1.timeSince)(buildPhaseStart, '⏱ Total CLI build time '));
31458
+ await (0, version_check_1.displayVersionWarningIfOutdated)(versionCheckPromise);
31459
+ (0, update_skills_1.displaySkillsWarningIfOutdated)();
31460
+ console.debug((0, log_utils_1.timeSince)(buildPhaseStart, 'Build time '));
31335
31461
  }
31336
31462
  catch (e) {
31337
31463
  // Show a version check warning even if the build failed.
31338
- await displayVersionWarning(versionCheckPromise);
31464
+ await (0, version_check_1.displayVersionWarningIfOutdated)(versionCheckPromise);
31465
+ (0, update_skills_1.displaySkillsWarningIfOutdated)();
31339
31466
  const errorMessage = (0, assertic_1.getMessageFromError)(e);
31340
31467
  console.debug(`Exiting with error: ${errorMessage}`);
31341
31468
  (0, process_utils_1.exitWithError)(errorMessage);
@@ -31443,12 +31570,16 @@ const enable_debug_decorator_utils_1 = __webpack_require__(8700);
31443
31570
  const global_utils_1 = __webpack_require__(6334);
31444
31571
  const http_1 = __webpack_require__(866);
31445
31572
  const shell_runner_1 = __webpack_require__(3089);
31573
+ const packageJson = __importStar(__webpack_require__(8330));
31446
31574
  const environment_1 = __webpack_require__(722);
31447
31575
  const process_utils_1 = __webpack_require__(8251);
31576
+ const version_check_1 = __webpack_require__(4827);
31448
31577
  async function deploy(consoleRegion, appId, bundlePath, apiKey, verbose, direct, isUserSpecifiedPath, skipBuild, internalApiKey, environmentId) {
31449
31578
  (0, global_utils_1.enableDebugLogs)(verbose || (0, global_utils_1.isDebugEnabled)());
31450
31579
  (0, logpipes_1.installConsoleOverrides)(enable_debug_decorator_utils_1.debugLogFilterPipe);
31580
+ const versionCheckPromise = (0, version_check_1.checkCliVersion)(packageJson.version);
31451
31581
  if (!direct && !isUserSpecifiedPath && !skipBuild) {
31582
+ // `squid build` handles all project types: TS, Python-only, and hybrid.
31452
31583
  console.log('Building code bundle...');
31453
31584
  await (0, shell_runner_1.runInShell)('npm run build');
31454
31585
  }
@@ -31477,8 +31608,10 @@ async function deploy(consoleRegion, appId, bundlePath, apiKey, verbose, direct,
31477
31608
  (0, process_utils_1.exitWithError)('Unable to deploy bundle:', await response.text());
31478
31609
  }
31479
31610
  console.log('Code deployed');
31611
+ await (0, version_check_1.displayVersionWarningIfOutdated)(versionCheckPromise);
31480
31612
  }
31481
31613
  catch (error) {
31614
+ await (0, version_check_1.displayVersionWarningIfOutdated)(versionCheckPromise);
31482
31615
  console.debug('Error deploying bundle:', error);
31483
31616
  const errorMessage = (0, assertic_1.getMessageFromError)(error, '');
31484
31617
  (0, process_utils_1.exitWithError)('Unable to deploy bundle:', errorMessage || error);
@@ -31690,10 +31823,12 @@ const init_webpack_1 = __webpack_require__(1134);
31690
31823
  const sample_1 = __webpack_require__(4328);
31691
31824
  const start_1 = __webpack_require__(496);
31692
31825
  const undeploy_1 = __webpack_require__(7097);
31826
+ const update_skills_1 = __webpack_require__(7286);
31693
31827
  const process_utils_1 = __webpack_require__(8251);
31694
31828
  const validate_1 = __webpack_require__(2246);
31695
31829
  function setupDotEnv(baseDir) {
31696
- dotenv.config({ path: path_1.default.resolve(baseDir || './', '.env') });
31830
+ const envPath = path_1.default.resolve(baseDir || './', '.env');
31831
+ dotenv.config({ path: envPath });
31697
31832
  }
31698
31833
  function run() {
31699
31834
  (0, yargs_1.default)(process.argv.slice(2))
@@ -31714,6 +31849,7 @@ function run() {
31714
31849
  setupInitWebpackCommand(yargs_1.default);
31715
31850
  setupStartCommand(yargs_1.default);
31716
31851
  setupUndeployCommand(yargs_1.default);
31852
+ setupUpdateSkillsCommand(yargs_1.default);
31717
31853
  yargs_1.default.parse();
31718
31854
  }
31719
31855
  function setupStartCommand(yargs) {
@@ -31844,6 +31980,11 @@ function setupBuildCommand(yargs) {
31844
31980
  await (0, build_1.build)({ verbose: !!argv.verbose, dev: !!argv.dev, skipVersionCheck: !!argv['skip-version-check'] });
31845
31981
  });
31846
31982
  }
31983
+ function setupUpdateSkillsCommand(yargs) {
31984
+ yargs.command('update-skills', 'Updates Squid Claude skills to the latest version', () => { }, async () => {
31985
+ await (0, update_skills_1.updateSkills)();
31986
+ });
31987
+ }
31847
31988
  run();
31848
31989
  function attachAppIdOption(yargs, demandOption) {
31849
31990
  yargs.option('appId', {
@@ -31973,6 +32114,39 @@ function getConsoleRegionFromAppRegion(appRegion) {
31973
32114
 
31974
32115
  "use strict";
31975
32116
 
32117
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
32118
+ if (k2 === undefined) k2 = k;
32119
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32120
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
32121
+ desc = { enumerable: true, get: function() { return m[k]; } };
32122
+ }
32123
+ Object.defineProperty(o, k2, desc);
32124
+ }) : (function(o, m, k, k2) {
32125
+ if (k2 === undefined) k2 = k;
32126
+ o[k2] = m[k];
32127
+ }));
32128
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
32129
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32130
+ }) : function(o, v) {
32131
+ o["default"] = v;
32132
+ });
32133
+ var __importStar = (this && this.__importStar) || (function () {
32134
+ var ownKeys = function(o) {
32135
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32136
+ var ar = [];
32137
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32138
+ return ar;
32139
+ };
32140
+ return ownKeys(o);
32141
+ };
32142
+ return function (mod) {
32143
+ if (mod && mod.__esModule) return mod;
32144
+ var result = {};
32145
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32146
+ __setModuleDefault(result, mod);
32147
+ return result;
32148
+ };
32149
+ })();
31976
32150
  var __importDefault = (this && this.__importDefault) || function (mod) {
31977
32151
  return (mod && mod.__esModule) ? mod : { "default": mod };
31978
32152
  };
@@ -31987,14 +32161,18 @@ const utils_1 = __webpack_require__(1215);
31987
32161
  const enable_debug_decorator_utils_1 = __webpack_require__(8700);
31988
32162
  const global_utils_1 = __webpack_require__(6334);
31989
32163
  const shell_runner_1 = __webpack_require__(3089);
32164
+ const packageJson = __importStar(__webpack_require__(8330));
32165
+ const update_skills_1 = __webpack_require__(7286);
31990
32166
  const git_utils_1 = __webpack_require__(954);
31991
32167
  const logging_1 = __webpack_require__(443);
31992
32168
  const process_utils_1 = __webpack_require__(8251);
31993
32169
  const report_utils_1 = __webpack_require__(3066);
32170
+ const version_check_1 = __webpack_require__(4827);
31994
32171
  const ORG = 'squid-cloud-samples';
31995
32172
  async function initSample(consoleRegion, isOnPremConsole, dirPath, appId, apiKey, environmentId, squidDeveloperId, region, templateName, verbose = false) {
31996
32173
  (0, global_utils_1.enableDebugLogs)(verbose || (0, global_utils_1.isDebugEnabled)());
31997
32174
  (0, logpipes_1.installConsoleOverrides)(enable_debug_decorator_utils_1.debugLogFilterPipe);
32175
+ const versionCheckPromise = (0, version_check_1.checkCliVersion)(packageJson.version);
31998
32176
  try {
31999
32177
  await promises_1.default.stat(path_1.default.resolve(dirPath));
32000
32178
  (0, process_utils_1.exitWithError)(`Project already exists: ${dirPath}`);
@@ -32014,42 +32192,25 @@ async function initSample(consoleRegion, isOnPremConsole, dirPath, appId, apiKey
32014
32192
  catch {
32015
32193
  (0, process_utils_1.exitWithError)(`Unable to download repository: ${templateRepoPath}`);
32016
32194
  }
32017
- // Copy the squid-development and squid-react-development skills if they do not already exist.
32195
+ // Copy Squid skills if they don't already exist.
32018
32196
  const projectPath = path_1.default.resolve(dirPath);
32019
32197
  const skillsDir = path_1.default.join(projectPath, '.claude', 'skills');
32020
- const squidSkillSourcePath = path_1.default.join(__dirname, 'resources', 'claude', 'skills', 'squid-development');
32021
- const squidSkillDestPath = path_1.default.join(skillsDir, 'squid-development');
32022
- if (await (0, utils_1.checkFileOrDirExists)(squidSkillDestPath)) {
32023
- console.debug('Squid development skill already exists, skipping...');
32024
- }
32025
- else {
32026
- // Skill doesn't exist, copy it.
32027
- try {
32028
- await promises_1.default.mkdir(skillsDir, { recursive: true });
32029
- await promises_1.default.cp(squidSkillSourcePath, squidSkillDestPath, { recursive: true });
32030
- console.debug('Squid development skill copied successfully');
32031
- }
32032
- catch (error) {
32033
- // Non-fatal: log the error but continue with initialization.
32034
- console.debug(`Warning: Could not copy Squid development skill: ${(0, assertic_1.getMessageFromError)(error)}`);
32035
- }
32036
- }
32037
- // Copy the squid-react-development skill if it doesn't already exist.
32038
- const reactSkillSourcePath = path_1.default.join(__dirname, 'resources', 'claude', 'skills', 'squid-react-development');
32039
- const reactSkillDestPath = path_1.default.join(skillsDir, 'squid-react-development');
32040
- if (await (0, utils_1.checkFileOrDirExists)(reactSkillDestPath)) {
32041
- console.debug('Squid React development skill already exists, skipping...');
32042
- }
32043
- else {
32044
- // Skill doesn't exist, copy it.
32045
- try {
32046
- await promises_1.default.mkdir(skillsDir, { recursive: true });
32047
- await promises_1.default.cp(reactSkillSourcePath, reactSkillDestPath, { recursive: true });
32048
- console.debug('Squid React development skill copied successfully');
32198
+ for (const skillName of update_skills_1.SQUID_SKILLS) {
32199
+ const sourcePath = path_1.default.join(__dirname, 'resources', 'claude', 'skills', skillName);
32200
+ const destPath = path_1.default.join(skillsDir, skillName);
32201
+ if (await (0, utils_1.checkFileOrDirExists)(destPath)) {
32202
+ console.debug(`${skillName} skill already exists, skipping...`);
32049
32203
  }
32050
- catch (error) {
32051
- // Non-fatal: log the error but continue with initialization.
32052
- console.debug(`Warning: Could not copy Squid React development skill: ${(0, assertic_1.getMessageFromError)(error)}`);
32204
+ else {
32205
+ try {
32206
+ await promises_1.default.mkdir(skillsDir, { recursive: true });
32207
+ await promises_1.default.cp(sourcePath, destPath, { recursive: true });
32208
+ console.debug(`${skillName} skill copied successfully`);
32209
+ }
32210
+ catch (error) {
32211
+ // Non-fatal: log the error but continue with initialization.
32212
+ console.debug(`Warning: Could not copy ${skillName} skill: ${(0, assertic_1.getMessageFromError)(error)}`);
32213
+ }
32053
32214
  }
32054
32215
  }
32055
32216
  console.log('Installing dependencies...');
@@ -32112,6 +32273,7 @@ ${(0, logging_1.primary)('Done. Next steps:')}
32112
32273
  npm run start
32113
32274
  `);
32114
32275
  }
32276
+ await (0, version_check_1.displayVersionWarningIfOutdated)(versionCheckPromise);
32115
32277
  }
32116
32278
 
32117
32279
 
@@ -32160,52 +32322,111 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
32160
32322
  };
32161
32323
  Object.defineProperty(exports, "__esModule", ({ value: true }));
32162
32324
  exports.start = start;
32325
+ const fs_1 = __webpack_require__(9896);
32163
32326
  const fs = __importStar(__webpack_require__(1943));
32164
32327
  const path_1 = __importDefault(__webpack_require__(6928));
32165
32328
  const resolve_1 = __webpack_require__(412);
32166
32329
  const shell_runner_1 = __webpack_require__(3089);
32330
+ const cliPackageJson = __importStar(__webpack_require__(8330));
32331
+ const update_skills_1 = __webpack_require__(7286);
32167
32332
  const logging_1 = __webpack_require__(443);
32168
32333
  const process_utils_1 = __webpack_require__(8251);
32169
32334
  const validate_1 = __webpack_require__(2246);
32335
+ const version_check_1 = __webpack_require__(4827);
32170
32336
  async function start() {
32171
- const packageJson = await (0, validate_1.validateSquidProject)();
32337
+ const projectInfo = await (0, validate_1.validateSquidProject)();
32338
+ const versionCheckPromise = (0, version_check_1.checkCliVersion)(cliPackageJson.version);
32172
32339
  console.log((0, logging_1.primary)('Please note:'), 'to debug your application, you need to run the "start" npm script in your IDE in debug mode');
32340
+ await (0, version_check_1.displayVersionWarningIfOutdated)(versionCheckPromise);
32341
+ (0, update_skills_1.displaySkillsWarningIfOutdated)();
32173
32342
  console.log((0, logging_1.primary)('Starting...'));
32343
+ if (projectInfo.isPython && !projectInfo.isHybrid) {
32344
+ await startPython();
32345
+ return;
32346
+ }
32347
+ // For hybrid projects, log Python detection
32348
+ if (projectInfo.isHybrid) {
32349
+ console.log((0, logging_1.primary)('Hybrid project detected:'), 'TypeScript + Python');
32350
+ }
32351
+ const packageJson = projectInfo.packageJson;
32352
+ // Check for local development override
32353
+ const localBackendPath = process.env['SQUID_LOCAL_BACKEND_PATH'];
32354
+ if (localBackendPath) {
32355
+ // Local development mode: use nest start from local-backend with SQUID_BACKEND_PATH
32356
+ console.log('Using local-backend from SQUID_LOCAL_BACKEND_PATH:', localBackendPath);
32357
+ const backendPath = process.cwd();
32358
+ await (0, shell_runner_1.runInShell)(`npx nest start --exec "node -r ts-node/register -r tsconfig-paths/register src/main.ts"`, true, localBackendPath, { SQUID_BACKEND_PATH: backendPath });
32359
+ return;
32360
+ }
32174
32361
  const modulePath = await (0, resolve_1.findModulePath)('@squidcloud/local-backend');
32175
32362
  if (!modulePath) {
32176
32363
  (0, process_utils_1.exitWithError)(validate_1.INVALID_PROJECT_ERROR);
32177
32364
  }
32178
32365
  if (packageJson.scripts?.['start-squid']) {
32179
32366
  await (0, shell_runner_1.runInShell)(`npm run start-squid`);
32367
+ return;
32180
32368
  }
32181
- else {
32182
- // Detect if CLI is running as TypeScript (dev mode)
32183
- const isDevMode = __filename.endsWith('.ts');
32184
- let mainPath;
32185
- // In dev mode, try to use local-backend source files
32186
- if (isDevMode) {
32187
- const localBackendSrc = path_1.default.resolve(__dirname, '../../local-backend/src/main.ts');
32188
- try {
32189
- await fs.access(localBackendSrc);
32190
- mainPath = localBackendSrc;
32191
- console.log((0, logging_1.primary)('Dev mode detected:'), `using local-backend source files: ${mainPath}`);
32192
- }
32193
- catch (_) {
32194
- // Fall back to built version if source files not found
32195
- mainPath = path_1.default.resolve(modulePath, '../main.js');
32196
- }
32197
- }
32198
- else {
32199
- mainPath = path_1.default.resolve(modulePath, '../main.js');
32200
- }
32369
+ // Detect if CLI is running as TypeScript (dev mode)
32370
+ const isDevMode = __filename.endsWith('.ts');
32371
+ let mainPath;
32372
+ // In dev mode, try to use local-backend source files
32373
+ if (isDevMode) {
32374
+ const localBackendSrc = path_1.default.resolve(__dirname, '../../local-backend/src/main.ts');
32201
32375
  try {
32202
- await fs.access(mainPath);
32203
- await (0, shell_runner_1.runInShell)(`npx nodemon --watch ./src --watch ./.env --ext ts,js,json --quiet --exec node -r ts-node/register -r tsconfig-paths/register "${mainPath}"`);
32376
+ await fs.access(localBackendSrc);
32377
+ mainPath = localBackendSrc;
32378
+ console.log((0, logging_1.primary)('Dev mode detected:'), `using local-backend source files: ${mainPath}`);
32204
32379
  }
32205
32380
  catch (_) {
32206
- (0, process_utils_1.exitWithError)(validate_1.INVALID_PROJECT_ERROR);
32381
+ // Fall back to built version if source files not found
32382
+ mainPath = path_1.default.resolve(modulePath, '../main.js');
32207
32383
  }
32208
32384
  }
32385
+ else {
32386
+ mainPath = path_1.default.resolve(modulePath, '../main.js');
32387
+ }
32388
+ try {
32389
+ await fs.access(mainPath);
32390
+ const ext = projectInfo.isHybrid ? 'py,ts,js,json' : 'ts,js,json';
32391
+ const ignore = projectInfo.isHybrid ? ' --ignore __python_deps__' : '';
32392
+ await (0, shell_runner_1.runInShell)(`npx nodemon --watch ./src --watch ./.env${ignore} --ext ${ext} --quiet --exec node -r ts-node/register -r tsconfig-paths/register "${mainPath}"`);
32393
+ }
32394
+ catch (_) {
32395
+ (0, process_utils_1.exitWithError)(validate_1.INVALID_PROJECT_ERROR);
32396
+ }
32397
+ }
32398
+ async function startPython() {
32399
+ console.log((0, logging_1.primary)('Python backend detected.'));
32400
+ const backendPath = process.cwd();
32401
+ // Resolve local-backend path (realpathSync handles symlinks from npm link)
32402
+ const modulePath = await (0, resolve_1.findModulePath)('@squidcloud/local-backend');
32403
+ let localBackendDir;
32404
+ const isDevMode = __filename.endsWith('.ts');
32405
+ if (isDevMode) {
32406
+ localBackendDir = path_1.default.resolve(__dirname, '../../local-backend');
32407
+ }
32408
+ else if (modulePath) {
32409
+ // modulePath points to the package entry (e.g. dist/index.js), resolve to package root
32410
+ localBackendDir = (0, fs_1.realpathSync)(path_1.default.resolve(modulePath, '..'));
32411
+ }
32412
+ else {
32413
+ (0, process_utils_1.exitWithError)(validate_1.INVALID_PROJECT_ERROR);
32414
+ }
32415
+ const tsconfigPath = path_1.default.resolve(localBackendDir, 'tsconfig.json');
32416
+ const mainPath = isDevMode ? path_1.default.resolve(localBackendDir, 'src/main.ts') : path_1.default.resolve(localBackendDir, 'main.js');
32417
+ // Python requirements are installed by the local-backend process (with --target for isolation).
32418
+ // No global pip install needed here.
32419
+ const envVars = {
32420
+ SQUID_BACKEND_PATH: backendPath,
32421
+ TS_NODE_PROJECT: tsconfigPath,
32422
+ };
32423
+ try {
32424
+ await fs.access(mainPath);
32425
+ await (0, shell_runner_1.runInShell)(`npx nodemon --watch ./src --watch ./.env --ignore __python_deps__ --ext py,ts,js,json --quiet --exec node -r ts-node/register -r tsconfig-paths/register "${mainPath}"`, true, backendPath, envVars);
32426
+ }
32427
+ catch (_) {
32428
+ (0, process_utils_1.exitWithError)(validate_1.INVALID_PROJECT_ERROR);
32429
+ }
32209
32430
  }
32210
32431
 
32211
32432
 
@@ -32250,6 +32471,149 @@ async function undeploy(consoleRegion, appId, apiKey, environmentId) {
32250
32471
  }
32251
32472
 
32252
32473
 
32474
+ /***/ },
32475
+
32476
+ /***/ 7286
32477
+ (__unused_webpack_module, exports, __webpack_require__) {
32478
+
32479
+ "use strict";
32480
+
32481
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
32482
+ if (k2 === undefined) k2 = k;
32483
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32484
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
32485
+ desc = { enumerable: true, get: function() { return m[k]; } };
32486
+ }
32487
+ Object.defineProperty(o, k2, desc);
32488
+ }) : (function(o, m, k, k2) {
32489
+ if (k2 === undefined) k2 = k;
32490
+ o[k2] = m[k];
32491
+ }));
32492
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
32493
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32494
+ }) : function(o, v) {
32495
+ o["default"] = v;
32496
+ });
32497
+ var __importStar = (this && this.__importStar) || (function () {
32498
+ var ownKeys = function(o) {
32499
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32500
+ var ar = [];
32501
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32502
+ return ar;
32503
+ };
32504
+ return ownKeys(o);
32505
+ };
32506
+ return function (mod) {
32507
+ if (mod && mod.__esModule) return mod;
32508
+ var result = {};
32509
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32510
+ __setModuleDefault(result, mod);
32511
+ return result;
32512
+ };
32513
+ })();
32514
+ var __importDefault = (this && this.__importDefault) || function (mod) {
32515
+ return (mod && mod.__esModule) ? mod : { "default": mod };
32516
+ };
32517
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
32518
+ exports.SQUID_SKILLS = void 0;
32519
+ exports.getSkillsNeedingUpdate = getSkillsNeedingUpdate;
32520
+ exports.displaySkillsWarningIfOutdated = displaySkillsWarningIfOutdated;
32521
+ exports.updateSkills = updateSkills;
32522
+ const chalk_1 = __importDefault(__webpack_require__(7459));
32523
+ const fsSync = __importStar(__webpack_require__(9896));
32524
+ const fs = __importStar(__webpack_require__(1943));
32525
+ const path_1 = __importDefault(__webpack_require__(6928));
32526
+ const logging_1 = __webpack_require__(443);
32527
+ const validate_1 = __webpack_require__(2246);
32528
+ /** List of Squid-owned skills that can be copied/updated. */
32529
+ exports.SQUID_SKILLS = ['squid-development', 'squid-react-development'];
32530
+ /**
32531
+ * Gets all files in a directory, sorted for consistent ordering.
32532
+ */
32533
+ function getFilesSorted(dir) {
32534
+ const files = fsSync.readdirSync(dir, { recursive: true });
32535
+ return files.filter(file => fsSync.statSync(path_1.default.join(dir, file)).isFile()).sort();
32536
+ }
32537
+ /**
32538
+ * Checks if a specific skill needs to be updated.
32539
+ * Returns true if project is missing skill or any CLI file is missing/different.
32540
+ * Extra files in project are ignored
32541
+ */
32542
+ function skillNeedsUpdate(skillName) {
32543
+ const projectSkillPath = path_1.default.join(process.cwd(), '.claude', 'skills', skillName);
32544
+ const cliSkillPath = path_1.default.join(__dirname, 'resources', 'claude', 'skills', skillName);
32545
+ // If CLI doesn't have this skill bundled, nothing to update.
32546
+ if (!fsSync.existsSync(cliSkillPath)) {
32547
+ return false;
32548
+ }
32549
+ // If project doesn't have this skill but CLI does, it needs to be added.
32550
+ if (!fsSync.existsSync(projectSkillPath)) {
32551
+ return true;
32552
+ }
32553
+ // Check each CLI file exists in project with same content.
32554
+ const cliFiles = getFilesSorted(cliSkillPath);
32555
+ for (const file of cliFiles) {
32556
+ const cliFilePath = path_1.default.join(cliSkillPath, file);
32557
+ const projectFilePath = path_1.default.join(projectSkillPath, file);
32558
+ // Project missing a file that CLI has.
32559
+ if (!fsSync.existsSync(projectFilePath)) {
32560
+ return true;
32561
+ }
32562
+ // Content differs (normalize line endings for cross-platform compatibility).
32563
+ const cliContent = fsSync.readFileSync(cliFilePath, 'utf8').replace(/\r\n/g, '\n');
32564
+ const projectContent = fsSync.readFileSync(projectFilePath, 'utf8').replace(/\r\n/g, '\n');
32565
+ if (cliContent !== projectContent) {
32566
+ return true;
32567
+ }
32568
+ }
32569
+ return false;
32570
+ }
32571
+ /**
32572
+ * Returns a list of skills that need to be updated (missing or outdated).
32573
+ */
32574
+ function getSkillsNeedingUpdate() {
32575
+ return exports.SQUID_SKILLS.filter(skillNeedsUpdate);
32576
+ }
32577
+ function displaySkillsWarningIfOutdated() {
32578
+ try {
32579
+ const skillsToUpdate = getSkillsNeedingUpdate();
32580
+ if (skillsToUpdate.length > 0) {
32581
+ console.warn(chalk_1.default.yellow(`⚠ Squid skills need updating (${skillsToUpdate.join(', ')}). Run: squid update-skills`));
32582
+ }
32583
+ }
32584
+ catch (_ignored) { }
32585
+ }
32586
+ /**
32587
+ * Updates all Squid-owned skills to the latest version bundled with the CLI.
32588
+ */
32589
+ async function updateSkills() {
32590
+ await (0, validate_1.validateSquidProject)();
32591
+ const skillsToUpdate = getSkillsNeedingUpdate();
32592
+ if (skillsToUpdate.length === 0) {
32593
+ console.log(chalk_1.default.green('✓ Skills already up to date'));
32594
+ return;
32595
+ }
32596
+ const skillsDir = path_1.default.join(process.cwd(), '.claude', 'skills');
32597
+ let updatedCount = 0;
32598
+ for (const skillName of skillsToUpdate) {
32599
+ const cliSkillPath = path_1.default.join(__dirname, 'resources', 'claude', 'skills', skillName);
32600
+ const projectSkillPath = path_1.default.join(skillsDir, skillName);
32601
+ try {
32602
+ await fs.mkdir(skillsDir, { recursive: true });
32603
+ await fs.cp(cliSkillPath, projectSkillPath, { recursive: true });
32604
+ console.log(`Updated ${(0, logging_1.primary)(skillName)} skill`);
32605
+ updatedCount++;
32606
+ }
32607
+ catch (_error) {
32608
+ console.warn(`Warning: Could not update ${skillName} skill`);
32609
+ }
32610
+ }
32611
+ if (updatedCount > 0) {
32612
+ console.log(chalk_1.default.green(`✓ ${updatedCount} skill(s) updated successfully`));
32613
+ }
32614
+ }
32615
+
32616
+
32253
32617
  /***/ },
32254
32618
 
32255
32619
  /***/ 954
@@ -32398,6 +32762,7 @@ async function reportLocalBackendInitialized(consoleRegion, appId, apiKey) {
32398
32762
  try {
32399
32763
  const headers = new Headers();
32400
32764
  headers.append('Authorization', `ApiKey ${apiKey}`);
32765
+ headers.append('Content-Type', 'application/json');
32401
32766
  const response = await fetch(url, { method: 'POST', body: JSON.stringify({ appId }), headers });
32402
32767
  if (!response.ok) {
32403
32768
  const responseText = await response.text();
@@ -32477,15 +32842,40 @@ exports.validateSquidProject = validateSquidProject;
32477
32842
  const promises_1 = __importDefault(__webpack_require__(1943));
32478
32843
  const process_utils_1 = __webpack_require__(8251);
32479
32844
  exports.INVALID_PROJECT_ERROR = 'Please make sure you are running this command in your Squid project directory';
32845
+ async function fileExists(filePath) {
32846
+ try {
32847
+ await promises_1.default.access(filePath);
32848
+ return true;
32849
+ }
32850
+ catch {
32851
+ return false;
32852
+ }
32853
+ }
32480
32854
  async function validateSquidProject() {
32855
+ const hasPython = (await fileExists('src/main.py')) || (await fileExists('src/index.py'));
32856
+ const hasTs = await fileExists('src/index.ts');
32857
+ const isHybrid = hasPython && hasTs;
32858
+ const isPythonOnly = hasPython && !hasTs;
32859
+ if (isPythonOnly) {
32860
+ // Python-only projects may not have package.json with start scripts
32861
+ let packageJson = {};
32862
+ try {
32863
+ const raw = await promises_1.default.readFile('package.json', 'utf8');
32864
+ packageJson = JSON.parse(raw);
32865
+ }
32866
+ catch {
32867
+ // No package.json is fine for Python projects
32868
+ }
32869
+ return { packageJson, isPython: true, isHybrid: false };
32870
+ }
32481
32871
  try {
32482
- const packageJson = await promises_1.default.readFile('package.json', 'utf8');
32483
- const parsedJson = JSON.parse(packageJson);
32872
+ const packageJsonRaw = await promises_1.default.readFile('package.json', 'utf8');
32873
+ const parsedJson = JSON.parse(packageJsonRaw);
32484
32874
  const { scripts } = parsedJson;
32485
32875
  if (!scripts?.['start-squid'] && !scripts?.['start']) {
32486
32876
  (0, process_utils_1.exitWithError)(exports.INVALID_PROJECT_ERROR);
32487
32877
  }
32488
- return parsedJson;
32878
+ return { packageJson: parsedJson, isPython: hasPython, isHybrid };
32489
32879
  }
32490
32880
  catch (_) {
32491
32881
  (0, process_utils_1.exitWithError)(exports.INVALID_PROJECT_ERROR);
@@ -32505,6 +32895,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
32505
32895
  };
32506
32896
  Object.defineProperty(exports, "__esModule", ({ value: true }));
32507
32897
  exports.checkCliVersion = checkCliVersion;
32898
+ exports.displayVersionWarningIfOutdated = displayVersionWarningIfOutdated;
32508
32899
  const assertic_1 = __webpack_require__(3205);
32509
32900
  const chalk_1 = __importDefault(__webpack_require__(7459));
32510
32901
  const time_units_1 = __webpack_require__(1929);
@@ -32581,6 +32972,15 @@ function parseVersion(version) {
32581
32972
  function formatWarningMessage(currentVersion, latestVersion) {
32582
32973
  return chalk_1.default.yellow(`⚠ Your @squidcloud/cli (${currentVersion}) is outdated. Latest: ${latestVersion}. Update: npm install -g @squidcloud/cli@latest`);
32583
32974
  }
32975
+ async function displayVersionWarningIfOutdated(versionCheckPromise) {
32976
+ try {
32977
+ const versionWarning = await versionCheckPromise;
32978
+ if (versionWarning) {
32979
+ console.warn(versionWarning);
32980
+ }
32981
+ }
32982
+ catch (_ignored) { }
32983
+ }
32584
32984
 
32585
32985
 
32586
32986
  /***/ },
@@ -36921,7 +37321,7 @@ terminalLink.stderr.isSupported = supports_hyperlinks.stderr;
36921
37321
  (module) {
36922
37322
 
36923
37323
  "use strict";
36924
- module.exports = /*#__PURE__*/JSON.parse('{"name":"dotenv","version":"16.6.1","description":"Loads environment variables from .env file","main":"lib/main.js","types":"lib/main.d.ts","exports":{".":{"types":"./lib/main.d.ts","require":"./lib/main.js","default":"./lib/main.js"},"./config":"./config.js","./config.js":"./config.js","./lib/env-options":"./lib/env-options.js","./lib/env-options.js":"./lib/env-options.js","./lib/cli-options":"./lib/cli-options.js","./lib/cli-options.js":"./lib/cli-options.js","./package.json":"./package.json"},"scripts":{"dts-check":"tsc --project tests/types/tsconfig.json","lint":"standard","pretest":"npm run lint && npm run dts-check","test":"tap run --allow-empty-coverage --disable-coverage --timeout=60000","test:coverage":"tap run --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov","prerelease":"npm test","release":"standard-version"},"repository":{"type":"git","url":"git://github.com/motdotla/dotenv.git"},"homepage":"https://github.com/motdotla/dotenv#readme","funding":"https://dotenvx.com","keywords":["dotenv","env",".env","environment","variables","config","settings"],"readmeFilename":"README.md","license":"BSD-2-Clause","devDependencies":{"@types/node":"^18.11.3","decache":"^4.6.2","sinon":"^14.0.1","standard":"^17.0.0","standard-version":"^9.5.0","tap":"^19.2.0","typescript":"^4.8.4"},"engines":{"node":">=12"},"browser":{"fs":false}}');
37324
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"dotenv","version":"17.3.1","description":"Loads environment variables from .env file","main":"lib/main.js","types":"lib/main.d.ts","exports":{".":{"types":"./lib/main.d.ts","require":"./lib/main.js","default":"./lib/main.js"},"./config":"./config.js","./config.js":"./config.js","./lib/env-options":"./lib/env-options.js","./lib/env-options.js":"./lib/env-options.js","./lib/cli-options":"./lib/cli-options.js","./lib/cli-options.js":"./lib/cli-options.js","./package.json":"./package.json"},"scripts":{"dts-check":"tsc --project tests/types/tsconfig.json","lint":"standard","pretest":"npm run lint && npm run dts-check","test":"tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000","test:coverage":"tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov","prerelease":"npm test","release":"standard-version"},"repository":{"type":"git","url":"git://github.com/motdotla/dotenv.git"},"homepage":"https://github.com/motdotla/dotenv#readme","funding":"https://dotenvx.com","keywords":["dotenv","env",".env","environment","variables","config","settings"],"readmeFilename":"README.md","license":"BSD-2-Clause","devDependencies":{"@types/node":"^18.11.3","decache":"^4.6.2","sinon":"^14.0.1","standard":"^17.0.0","standard-version":"^9.5.0","tap":"^19.2.0","typescript":"^4.8.4"},"engines":{"node":">=12"},"browser":{"fs":false}}');
36925
37325
 
36926
37326
  /***/ },
36927
37327
 
@@ -36937,7 +37337,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"name":"seek-bzip","version":"1.0.6",
36937
37337
  (module) {
36938
37338
 
36939
37339
  "use strict";
36940
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@squidcloud/cli","version":"1.0.449","description":"The Squid CLI","main":"dist/index.js","scripts":{"start":"node dist/index.js","start-ts":"ts-node -r tsconfig-paths/register src/index.ts","prebuild":"rimraf dist","build":"webpack --mode=production","build:dev":"webpack --mode=development","lint":"eslint","link":"npm run build && chmod 755 dist/index.js && npm link","watch":"webpack --watch","deploy":"npm run build && npm pack --silent | xargs -I {} mv {} package.tgz && npm install -g package.tgz && rm -rf package.tgz","publish:public":"npm run build && npm publish --access public"},"files":["dist/**/*"],"bin":{"squid":"dist/index.js"},"keywords":[],"author":"","license":"ISC","engines":{"node":">=18.0.0"},"dependencies":{"@squidcloud/local-backend":"^1.0.449","adm-zip":"^0.5.16","copy-webpack-plugin":"^12.0.2","decompress":"^4.2.1","logpipes":"^1.11.0","nodemon":"^3.1.9","terser-webpack-plugin":"^5.3.10","ts-loader":"^9.5.1","ts-node":"^10.9.2","tsconfig-paths":"^4.2.0","tsconfig-paths-webpack-plugin":"^4.1.0","webpack":"^5.101.3","zip-webpack-plugin":"^4.0.1"},"devDependencies":{"@types/adm-zip":"^0.5.7","@types/decompress":"^4.2.7","@types/node":"^20.19.9","terminal-link":"^3.0.0"}}');
37340
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@squidcloud/cli","version":"1.0.451","description":"The Squid CLI","main":"dist/index.js","scripts":{"start":"node dist/index.js","start-ts":"ts-node -r tsconfig-paths/register src/index.ts","prebuild":"rimraf dist","build":"webpack --mode=production","build:dev":"webpack --mode=development","lint":"eslint","link":"npm run build && chmod 755 dist/index.js && npm link","watch":"webpack --watch","deploy":"npm run build && npm pack --silent | xargs -I {} mv {} package.tgz && npm install -g package.tgz && rm -rf package.tgz","publish:public":"npm run build && npm publish --access public"},"files":["dist/**/*"],"bin":{"squid":"dist/index.js"},"keywords":[],"author":"","license":"ISC","engines":{"node":">=18.0.0"},"dependencies":{"@squidcloud/local-backend":"^1.0.451","adm-zip":"^0.5.16","copy-webpack-plugin":"^12.0.2","decompress":"^4.2.1","logpipes":"^1.11.0","nodemon":"^3.1.9","terser-webpack-plugin":"^5.3.10","ts-loader":"^9.5.1","ts-node":"^10.9.2","tsconfig-paths":"^4.2.0","tsconfig-paths-webpack-plugin":"^4.1.0","webpack":"^5.101.3","zip-webpack-plugin":"^4.0.1"},"devDependencies":{"@types/adm-zip":"^0.5.7","@types/decompress":"^4.2.7","@types/node":"^20.19.9","terminal-link":"^3.0.0"}}');
36941
37341
 
36942
37342
  /***/ }
36943
37343