@base44-preview/cli 0.0.34-pr.228.ff14dd8 → 0.0.34-pr.259.5b805fc

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/README.md CHANGED
@@ -56,6 +56,11 @@ The CLI will guide you through project setup. For step-by-step tutorials, see th
56
56
  | [`connectors pull`](https://docs.base44.com/developers/references/cli/commands/connectors-pull) | Pull connectors from Base44 to local files |
57
57
  | [`connectors push`](https://docs.base44.com/developers/references/cli/commands/connectors-push) | Push local connectors to Base44 |
58
58
  | [`entities push`](https://docs.base44.com/developers/references/cli/commands/entities-push) | Push local entities to Base44 |
59
+ | [`entities records list`](https://docs.base44.com/developers/references/cli/commands/entities-records) | List entity records |
60
+ | [`entities records get`](https://docs.base44.com/developers/references/cli/commands/entities-records) | Get a single entity record by ID |
61
+ | [`entities records create`](https://docs.base44.com/developers/references/cli/commands/entities-records) | Create a new entity record |
62
+ | [`entities records update`](https://docs.base44.com/developers/references/cli/commands/entities-records) | Update an entity record |
63
+ | [`entities records delete`](https://docs.base44.com/developers/references/cli/commands/entities-records) | Delete an entity record |
59
64
  | [`functions deploy`](https://docs.base44.com/developers/references/cli/commands/functions-deploy) | Deploy local functions to Base44 |
60
65
  | [`site deploy`](https://docs.base44.com/developers/references/cli/commands/site-deploy) | Deploy built site files to Base44 hosting |
61
66
  | [`site open`](https://docs.base44.com/developers/references/cli/commands/site-open) | Open the published site in your browser |
package/dist/cli/index.js CHANGED
@@ -185389,16 +185389,18 @@ async function fetchAgents() {
185389
185389
  }
185390
185390
  // src/core/resources/agent/config.ts
185391
185391
  import { join as join3 } from "node:path";
185392
- import { isDeepStrictEqual } from "node:util";
185392
+ function toFileSlug(name2) {
185393
+ return name2.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
185394
+ }
185393
185395
  async function readAgentFile(agentPath) {
185394
- const raw2 = await readJsonFile(agentPath);
185395
- const result = AgentConfigSchema.safeParse(raw2);
185396
+ const parsed = await readJsonFile(agentPath);
185397
+ const result = AgentConfigSchema.safeParse(parsed);
185396
185398
  if (!result.success) {
185397
185399
  throw new SchemaValidationError("Invalid agent file", result.error, agentPath);
185398
185400
  }
185399
- return { data: result.data, raw: raw2 };
185401
+ return result.data;
185400
185402
  }
185401
- async function readAgentFiles(agentsDir) {
185403
+ async function readAllAgents(agentsDir) {
185402
185404
  if (!await pathExists(agentsDir)) {
185403
185405
  return [];
185404
185406
  }
@@ -185406,49 +185408,37 @@ async function readAgentFiles(agentsDir) {
185406
185408
  cwd: agentsDir,
185407
185409
  absolute: true
185408
185410
  });
185409
- return await Promise.all(files.map(async (filePath) => {
185410
- const { data, raw: raw2 } = await readAgentFile(filePath);
185411
- return { data, raw: raw2, filePath };
185412
- }));
185413
- }
185414
- async function readAllAgents(agentsDir) {
185415
- const entries = await readAgentFiles(agentsDir);
185411
+ const agents = await Promise.all(files.map((filePath) => readAgentFile(filePath)));
185416
185412
  const names = new Set;
185417
- for (const { data } of entries) {
185418
- if (names.has(data.name)) {
185419
- throw new Error(`Duplicate agent name "${data.name}"`);
185413
+ for (const agent of agents) {
185414
+ if (names.has(agent.name)) {
185415
+ throw new Error(`Duplicate agent name "${agent.name}"`);
185420
185416
  }
185421
- names.add(data.name);
185417
+ names.add(agent.name);
185422
185418
  }
185423
- return entries.map((e2) => e2.data);
185419
+ return agents;
185424
185420
  }
185425
185421
  async function writeAgents(agentsDir, remoteAgents) {
185426
- const entries = await readAgentFiles(agentsDir);
185427
- const nameToEntry = new Map;
185428
- for (const entry of entries) {
185429
- if (nameToEntry.has(entry.data.name)) {
185430
- throw new Error(`Duplicate agent name "${entry.data.name}"`);
185431
- }
185432
- nameToEntry.set(entry.data.name, entry);
185433
- }
185422
+ const existingAgents = await readAllAgents(agentsDir);
185434
185423
  const newNames = new Set(remoteAgents.map((a) => a.name));
185435
- const deleted = [];
185436
- for (const [name2, entry] of nameToEntry) {
185437
- if (!newNames.has(name2)) {
185438
- await deleteFile(entry.filePath);
185439
- deleted.push(name2);
185424
+ const toDelete = existingAgents.filter((a) => !newNames.has(a.name));
185425
+ for (const agent of toDelete) {
185426
+ const slug = toFileSlug(agent.name);
185427
+ const files = await globby(`${slug}.${CONFIG_FILE_EXTENSION_GLOB}`, {
185428
+ cwd: agentsDir,
185429
+ absolute: true
185430
+ });
185431
+ for (const filePath of files) {
185432
+ await deleteFile(filePath);
185440
185433
  }
185441
185434
  }
185442
- const written = [];
185443
185435
  for (const agent of remoteAgents) {
185444
- const existing = nameToEntry.get(agent.name);
185445
- if (existing && isDeepStrictEqual(existing.raw, agent)) {
185446
- continue;
185447
- }
185448
- const filePath = existing?.filePath ?? join3(agentsDir, `${agent.name}.${CONFIG_FILE_EXTENSION}`);
185436
+ const slug = toFileSlug(agent.name);
185437
+ const filePath = join3(agentsDir, `${slug}.${CONFIG_FILE_EXTENSION}`);
185449
185438
  await writeJsonFile(filePath, agent);
185450
- written.push(agent.name);
185451
185439
  }
185440
+ const written = remoteAgents.map((a) => a.name);
185441
+ const deleted = toDelete.map((a) => a.name);
185452
185442
  return { written, deleted };
185453
185443
  }
185454
185444
  // src/core/resources/agent/resource.ts
@@ -185665,7 +185655,7 @@ async function removeConnector(integrationType) {
185665
185655
  }
185666
185656
  // src/core/resources/connector/config.ts
185667
185657
  import { join as join4 } from "node:path";
185668
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
185658
+ import { isDeepStrictEqual } from "node:util";
185669
185659
  async function readConnectorFile(connectorPath) {
185670
185660
  const parsed = await readJsonFile(connectorPath);
185671
185661
  const result = ConnectorResourceSchema.safeParse(parsed);
@@ -185726,7 +185716,7 @@ async function writeConnectors(connectorsDir, remoteConnectors) {
185726
185716
  type: connector.integrationType,
185727
185717
  scopes: connector.scopes
185728
185718
  };
185729
- if (existing && isDeepStrictEqual2(existing.data, localConnector)) {
185719
+ if (existing && isDeepStrictEqual(existing.data, localConnector)) {
185730
185720
  continue;
185731
185721
  }
185732
185722
  const filePath = existing?.filePath ?? join4(connectorsDir, `${connector.integrationType}.${CONFIG_FILE_EXTENSION}`);
@@ -185959,6 +185949,118 @@ async function pushEntities(entities) {
185959
185949
  }
185960
185950
  return syncEntities(entities);
185961
185951
  }
185952
+ // src/core/resources/entity/records-schema.ts
185953
+ var EntityRecordSchema = exports_external.looseObject({
185954
+ id: exports_external.string(),
185955
+ created_date: exports_external.string(),
185956
+ updated_date: exports_external.string(),
185957
+ created_by: exports_external.string().optional()
185958
+ });
185959
+ var DeleteRecordResponseSchema = exports_external.object({
185960
+ success: exports_external.boolean()
185961
+ });
185962
+
185963
+ // src/core/resources/entity/records-api.ts
185964
+ async function listRecords(entityName, options = {}) {
185965
+ const client = await getAppUserClient();
185966
+ const searchParams = new URLSearchParams;
185967
+ if (options.filter) {
185968
+ searchParams.set("q", options.filter);
185969
+ }
185970
+ if (options.sort) {
185971
+ searchParams.set("sort", options.sort);
185972
+ }
185973
+ if (options.limit !== undefined) {
185974
+ searchParams.set("limit", String(options.limit));
185975
+ }
185976
+ if (options.skip !== undefined) {
185977
+ searchParams.set("skip", String(options.skip));
185978
+ }
185979
+ if (options.fields) {
185980
+ searchParams.set("fields", options.fields);
185981
+ }
185982
+ let response;
185983
+ try {
185984
+ response = await client.get(`entities/${entityName}`, {
185985
+ searchParams
185986
+ });
185987
+ } catch (error48) {
185988
+ throw await ApiError.fromHttpError(error48, "listing records");
185989
+ }
185990
+ const json2 = await response.json();
185991
+ const records = Array.isArray(json2) ? json2 : [];
185992
+ return records.map((record2) => {
185993
+ const result = EntityRecordSchema.safeParse(record2);
185994
+ if (!result.success) {
185995
+ throw new SchemaValidationError("Invalid record in response", result.error);
185996
+ }
185997
+ return result.data;
185998
+ });
185999
+ }
186000
+ async function getRecord(entityName, recordId) {
186001
+ const client = await getAppUserClient();
186002
+ let response;
186003
+ try {
186004
+ response = await client.get(`entities/${entityName}/${recordId}`);
186005
+ } catch (error48) {
186006
+ throw await ApiError.fromHttpError(error48, "getting record");
186007
+ }
186008
+ const json2 = await response.json();
186009
+ const result = EntityRecordSchema.safeParse(json2);
186010
+ if (!result.success) {
186011
+ throw new SchemaValidationError("Invalid record response", result.error);
186012
+ }
186013
+ return result.data;
186014
+ }
186015
+ async function createRecord(entityName, data) {
186016
+ const client = await getAppUserClient();
186017
+ let response;
186018
+ try {
186019
+ response = await client.post(`entities/${entityName}`, {
186020
+ json: data
186021
+ });
186022
+ } catch (error48) {
186023
+ throw await ApiError.fromHttpError(error48, "creating record");
186024
+ }
186025
+ const json2 = await response.json();
186026
+ const result = EntityRecordSchema.safeParse(json2);
186027
+ if (!result.success) {
186028
+ throw new SchemaValidationError("Invalid record in create response", result.error);
186029
+ }
186030
+ return result.data;
186031
+ }
186032
+ async function updateRecord(entityName, recordId, data) {
186033
+ const client = await getAppUserClient();
186034
+ let response;
186035
+ try {
186036
+ response = await client.put(`entities/${entityName}/${recordId}`, {
186037
+ json: data
186038
+ });
186039
+ } catch (error48) {
186040
+ throw await ApiError.fromHttpError(error48, "updating record");
186041
+ }
186042
+ const json2 = await response.json();
186043
+ const result = EntityRecordSchema.safeParse(json2);
186044
+ if (!result.success) {
186045
+ throw new SchemaValidationError("Invalid record in update response", result.error);
186046
+ }
186047
+ return result.data;
186048
+ }
186049
+ async function deleteRecord(entityName, recordId) {
186050
+ const client = await getAppUserClient();
186051
+ let response;
186052
+ try {
186053
+ response = await client.delete(`entities/${entityName}/${recordId}`);
186054
+ } catch (error48) {
186055
+ throw await ApiError.fromHttpError(error48, "deleting record");
186056
+ }
186057
+ const json2 = await response.json();
186058
+ const result = DeleteRecordResponseSchema.safeParse(json2);
186059
+ if (!result.success) {
186060
+ throw new SchemaValidationError("Invalid delete response", result.error);
186061
+ }
186062
+ return result.data;
186063
+ }
185962
186064
  // src/core/resources/entity/resource.ts
185963
186065
  var entityResource = {
185964
186066
  readAll: readAllEntities,
@@ -186547,6 +186649,21 @@ function getAppClient() {
186547
186649
  prefixUrl: new URL(`/api/apps/${id}/`, getBase44ApiUrl()).href
186548
186650
  });
186549
186651
  }
186652
+ async function getAppUserClient() {
186653
+ const appClient = getAppClient();
186654
+ const response = await appClient.get("auth/token");
186655
+ const { token } = await response.json();
186656
+ const { id } = getAppConfig();
186657
+ return distribution_default.create({
186658
+ prefixUrl: new URL(`/api/apps/${id}/`, getBase44ApiUrl()).href,
186659
+ headers: {
186660
+ "User-Agent": "Base44 CLI",
186661
+ Authorization: `Bearer ${token}`,
186662
+ "X-Bypass-RLS": "true",
186663
+ "X-Bypass-Entities-Filter-Limit": "true"
186664
+ }
186665
+ });
186666
+ }
186550
186667
  // src/core/clients/oauth-client.ts
186551
186668
  var oauthClient = distribution_default.create({
186552
186669
  prefixUrl: getBase44ApiUrl(),
@@ -194140,11 +194257,14 @@ async function pullAgentsAction() {
194140
194257
  successMessage: "Agents fetched successfully",
194141
194258
  errorMessage: "Failed to fetch agents"
194142
194259
  });
194143
- const { written, deleted } = await runTask("Syncing agent files", async () => {
194260
+ if (remoteAgents.items.length === 0) {
194261
+ return { outroMessage: "No agents found on Base44" };
194262
+ }
194263
+ const { written, deleted } = await runTask("Writing agent files", async () => {
194144
194264
  return await writeAgents(agentsDir, remoteAgents.items);
194145
194265
  }, {
194146
- successMessage: "Agent files synced successfully",
194147
- errorMessage: "Failed to sync agent files"
194266
+ successMessage: "Agent files written successfully",
194267
+ errorMessage: "Failed to write agent files"
194148
194268
  });
194149
194269
  if (written.length > 0) {
194150
194270
  R2.success(`Written: ${written.join(", ")}`);
@@ -194152,9 +194272,6 @@ async function pullAgentsAction() {
194152
194272
  if (deleted.length > 0) {
194153
194273
  R2.warn(`Deleted: ${deleted.join(", ")}`);
194154
194274
  }
194155
- if (written.length === 0 && deleted.length === 0) {
194156
- R2.info("All agents are already up to date");
194157
- }
194158
194275
  return {
194159
194276
  outroMessage: `Pulled ${remoteAgents.total} agents to ${agentsDir}`
194160
194277
  };
@@ -195074,9 +195191,150 @@ async function pushEntitiesAction() {
195074
195191
  return { outroMessage: "Entities pushed to Base44" };
195075
195192
  }
195076
195193
  function getEntitiesPushCommand(context) {
195077
- return new Command("entities").description("Manage project entities").addCommand(new Command("push").description("Push local entities to Base44").action(async () => {
195194
+ return new Command("push").description("Push local entity schemas to Base44").action(async () => {
195078
195195
  await runCommand(pushEntitiesAction, { requireAuth: true }, context);
195079
- }));
195196
+ });
195197
+ }
195198
+
195199
+ // src/cli/commands/entities/records/parseRecordData.ts
195200
+ var import_json52 = __toESM(require_lib(), 1);
195201
+ async function parseRecordData(options, exampleHint) {
195202
+ if (options.data && options.file) {
195203
+ throw new InvalidInputError("Cannot use both --data and --file. Choose one.", {
195204
+ hints: [
195205
+ {
195206
+ message: `Pass --data for inline JSON or --file for a JSON file path, not both.`
195207
+ }
195208
+ ]
195209
+ });
195210
+ }
195211
+ if (options.data) {
195212
+ try {
195213
+ return import_json52.default.parse(options.data);
195214
+ } catch {
195215
+ throw new InvalidInputError("Invalid JSON in --data flag. Provide valid JSON.", exampleHint ? { hints: [{ message: `Example: --data '${exampleHint}'` }] } : undefined);
195216
+ }
195217
+ }
195218
+ if (options.file) {
195219
+ return readJsonFile(options.file);
195220
+ }
195221
+ throw new InvalidInputError("Provide record data with --data or --file flag", exampleHint ? {
195222
+ hints: [
195223
+ {
195224
+ message: `Example: --data '${exampleHint}' or --file record.json`
195225
+ }
195226
+ ]
195227
+ } : undefined);
195228
+ }
195229
+
195230
+ // src/cli/commands/entities/records/create.ts
195231
+ async function createRecordAction(entityName, options) {
195232
+ const data = await parseRecordData(options, '{"name": "John", "email": "john@example.com"}');
195233
+ const record2 = await runTask(`Creating ${entityName} record...`, async () => {
195234
+ return await createRecord(entityName, data);
195235
+ }, {
195236
+ successMessage: `Created ${entityName} record`,
195237
+ errorMessage: `Failed to create ${entityName} record`
195238
+ });
195239
+ R2.info(JSON.stringify(record2, null, 2));
195240
+ return { outroMessage: `Record created with ID: ${record2.id}` };
195241
+ }
195242
+ function getRecordsCreateCommand(context) {
195243
+ return new Command("create").description("Create a new entity record").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").option("-d, --data <json>", "JSON object with record data").option("--file <path>", "Read record data from a JSON/JSONC file").action(async (entityName, options) => {
195244
+ await runCommand(() => createRecordAction(entityName, options), { requireAuth: true }, context);
195245
+ });
195246
+ }
195247
+
195248
+ // src/cli/commands/entities/records/delete.ts
195249
+ async function deleteRecordAction(entityName, recordId, options) {
195250
+ if (!options.yes) {
195251
+ const confirmed = await Re({
195252
+ message: `Delete ${entityName} record ${recordId}?`
195253
+ });
195254
+ if (confirmed !== true) {
195255
+ throw new CLIExitError(0);
195256
+ }
195257
+ }
195258
+ await runTask(`Deleting ${entityName} record...`, async () => {
195259
+ return await deleteRecord(entityName, recordId);
195260
+ }, {
195261
+ successMessage: `Deleted ${entityName} record`,
195262
+ errorMessage: `Failed to delete ${entityName} record`
195263
+ });
195264
+ return { outroMessage: `Record ${recordId} deleted` };
195265
+ }
195266
+ function getRecordsDeleteCommand(context) {
195267
+ return new Command("delete").description("Delete an entity record").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").argument("<record-id>", "ID of the record to delete").option("-y, --yes", "Skip confirmation prompt").action(async (entityName, recordId, options) => {
195268
+ await runCommand(() => deleteRecordAction(entityName, recordId, options), { requireAuth: true }, context);
195269
+ });
195270
+ }
195271
+
195272
+ // src/cli/commands/entities/records/get.ts
195273
+ async function getRecordAction(entityName, recordId) {
195274
+ const record2 = await runTask(`Fetching ${entityName} record...`, async () => {
195275
+ return await getRecord(entityName, recordId);
195276
+ }, {
195277
+ successMessage: `Fetched ${entityName} record`,
195278
+ errorMessage: `Failed to fetch ${entityName} record`
195279
+ });
195280
+ R2.info(JSON.stringify(record2, null, 2));
195281
+ return {};
195282
+ }
195283
+ function getRecordsGetCommand(context) {
195284
+ return new Command("get").description("Get a single entity record by ID").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").argument("<record-id>", "ID of the record").action(async (entityName, recordId) => {
195285
+ await runCommand(() => getRecordAction(entityName, recordId), { requireAuth: true }, context);
195286
+ });
195287
+ }
195288
+
195289
+ // src/cli/commands/entities/records/list.ts
195290
+ async function listRecordsAction(entityName, options) {
195291
+ const records = await runTask(`Fetching ${entityName} records...`, async () => {
195292
+ return await listRecords(entityName, {
195293
+ filter: options.filter,
195294
+ sort: options.sort,
195295
+ limit: options.limit ? Number(options.limit) : 50,
195296
+ skip: options.skip ? Number(options.skip) : undefined,
195297
+ fields: options.fields
195298
+ });
195299
+ }, {
195300
+ successMessage: `Fetched ${entityName} records`,
195301
+ errorMessage: `Failed to fetch ${entityName} records`
195302
+ });
195303
+ R2.info(JSON.stringify(records, null, 2));
195304
+ return { outroMessage: `Found ${records.length} ${entityName} record(s)` };
195305
+ }
195306
+ function getRecordsListCommand(context) {
195307
+ return new Command("list").description("List entity records").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").option("-f, --filter <json>", `JSON filter object (e.g. '{"status":"active"}' or '{"age":{"$gt":18}}')`).option("-s, --sort <field>", "Sort field name, prefix with - for descending (e.g. -created_date)").option("-l, --limit <n>", "Max number of records to return", "50").option("--skip <n>", "Number of records to skip (for pagination)").option("--fields <fields>", "Comma-separated list of fields to return (e.g. id,name,email)").action(async (entityName, options) => {
195308
+ await runCommand(() => listRecordsAction(entityName, options), { requireAuth: true }, context);
195309
+ });
195310
+ }
195311
+
195312
+ // src/cli/commands/entities/records/update.ts
195313
+ async function updateRecordAction(entityName, recordId, options) {
195314
+ const data = await parseRecordData(options, '{"status": "active"}');
195315
+ const record2 = await runTask(`Updating ${entityName} record...`, async () => {
195316
+ return await updateRecord(entityName, recordId, data);
195317
+ }, {
195318
+ successMessage: `Updated ${entityName} record`,
195319
+ errorMessage: `Failed to update ${entityName} record`
195320
+ });
195321
+ R2.info(JSON.stringify(record2, null, 2));
195322
+ return { outroMessage: `Record ${recordId} updated` };
195323
+ }
195324
+ function getRecordsUpdateCommand(context) {
195325
+ return new Command("update").description("Update an entity record").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").argument("<record-id>", "ID of the record to update").option("-d, --data <json>", "JSON object with fields to update").option("--file <path>", "Read update data from a JSON/JSONC file").action(async (entityName, recordId, options) => {
195326
+ await runCommand(() => updateRecordAction(entityName, recordId, options), { requireAuth: true }, context);
195327
+ });
195328
+ }
195329
+
195330
+ // src/cli/commands/entities/records/index.ts
195331
+ function getRecordsCommand(context) {
195332
+ return new Command("records").description("CRUD operations on entity records").addCommand(getRecordsListCommand(context)).addCommand(getRecordsGetCommand(context)).addCommand(getRecordsCreateCommand(context)).addCommand(getRecordsUpdateCommand(context)).addCommand(getRecordsDeleteCommand(context));
195333
+ }
195334
+
195335
+ // src/cli/commands/entities/index.ts
195336
+ function getEntitiesCommand(context) {
195337
+ return new Command("entities").description("Manage project entities").addCommand(getEntitiesPushCommand(context)).addCommand(getRecordsCommand(context));
195080
195338
  }
195081
195339
 
195082
195340
  // src/cli/commands/functions/deploy.ts
@@ -196343,7 +196601,7 @@ function createProgram(context) {
196343
196601
  program2.addCommand(getDeployCommand(context));
196344
196602
  program2.addCommand(getLinkCommand(context));
196345
196603
  program2.addCommand(getEjectCommand(context));
196346
- program2.addCommand(getEntitiesPushCommand(context));
196604
+ program2.addCommand(getEntitiesCommand(context));
196347
196605
  program2.addCommand(getAgentsCommand(context));
196348
196606
  program2.addCommand(getConnectorsCommand(context));
196349
196607
  program2.addCommand(getFunctionsDeployCommand(context));
@@ -200615,4 +200873,4 @@ export {
200615
200873
  CLIExitError
200616
200874
  };
200617
200875
 
200618
- //# debugId=199B0E01CD0F366764756E2164756E21
200876
+ //# debugId=50E9171D5E0E5F6864756E2164756E21