@lovelybunch/api 1.0.59 → 1.0.61

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.
@@ -18,7 +18,7 @@ function resolveAgent(model) {
18
18
  return 'codex';
19
19
  return 'claude';
20
20
  }
21
- function buildCommand(agent, instruction) {
21
+ function buildCommand(agent, instruction, options) {
22
22
  const quotedInstruction = shellQuote(instruction);
23
23
  switch (agent) {
24
24
  case 'gemini': {
@@ -26,12 +26,17 @@ function buildCommand(agent, instruction) {
26
26
  return { command: 'gemini', shellCommand: cmd };
27
27
  }
28
28
  case 'codex': {
29
- const cmd = `codex ${quotedInstruction} --dangerously-bypass-approvals-and-sandbox`;
30
- return { command: 'codex', shellCommand: cmd };
29
+ const baseCmd = `codex ${quotedInstruction} --dangerously-bypass-approvals-and-sandbox`;
30
+ const needsPseudoTty = options.runningAsRoot && process.platform !== 'win32';
31
+ const wrappedCmd = needsPseudoTty
32
+ ? `script -q -e -c ${shellQuote(baseCmd)} /dev/null`
33
+ : baseCmd;
34
+ return { command: 'codex', shellCommand: wrappedCmd };
31
35
  }
32
36
  case 'claude':
33
37
  default: {
34
- const cmd = `claude ${quotedInstruction} --dangerously-skip-permissions`;
38
+ const prefix = options.runningAsRoot ? 'IS_SANDBOX=1 ' : '';
39
+ const cmd = `${prefix}claude ${quotedInstruction} --dangerously-skip-permissions`;
35
40
  return { command: 'claude', shellCommand: cmd };
36
41
  }
37
42
  }
@@ -85,7 +90,8 @@ export class JobRunner {
85
90
  async run(job, runId) {
86
91
  const agent = resolveAgent(job.model);
87
92
  const instruction = this.buildInstruction(job, CLI_AGENT_LABEL[agent] || agent);
88
- const { shellCommand } = buildCommand(agent, instruction);
93
+ const runningAsRoot = typeof process.getuid === 'function' && process.getuid() === 0;
94
+ const { shellCommand } = buildCommand(agent, instruction, { runningAsRoot });
89
95
  const projectRoot = await this.projectRootPromise;
90
96
  const logPath = await this.ensureLogPath(job.id, runId);
91
97
  const logStream = createWriteStream(logPath, { flags: 'a' });
@@ -3,6 +3,24 @@ import { promises as fs } from 'fs';
3
3
  import path from 'path';
4
4
  import matter from 'gray-matter';
5
5
  const app = new Hono();
6
+ function normalizeTools(value) {
7
+ if (!value)
8
+ return undefined;
9
+ if (Array.isArray(value)) {
10
+ const cleaned = value
11
+ .map((item) => (typeof item === 'string' ? item.trim() : ''))
12
+ .filter((item) => Boolean(item));
13
+ return cleaned.length > 0 ? Array.from(new Set(cleaned)) : undefined;
14
+ }
15
+ if (typeof value === 'string') {
16
+ const cleaned = value
17
+ .split(',')
18
+ .map((item) => item.trim())
19
+ .filter(Boolean);
20
+ return cleaned.length > 0 ? Array.from(new Set(cleaned)) : undefined;
21
+ }
22
+ return undefined;
23
+ }
6
24
  function getAgentsPath() {
7
25
  let basePath;
8
26
  if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
@@ -47,11 +65,11 @@ app.get('/', async (c) => {
47
65
  const document = {
48
66
  filename,
49
67
  metadata: {
68
+ ...data,
50
69
  name: data.name || title,
51
70
  description: data.description || '',
52
71
  color: data.color,
53
- tools: data.tools,
54
- ...data
72
+ tools: normalizeTools(data.tools)
55
73
  },
56
74
  content,
57
75
  title
@@ -98,6 +116,13 @@ app.put('/', async (c) => {
98
116
  name: body.name !== undefined ? body.name : currentData.name,
99
117
  description: body.description !== undefined ? body.description : currentData.description
100
118
  };
119
+ const normalizedTools = normalizeTools(body.metadata?.tools ?? updatedMetadata.tools);
120
+ if (normalizedTools) {
121
+ updatedMetadata.tools = normalizedTools;
122
+ }
123
+ else {
124
+ delete updatedMetadata.tools;
125
+ }
101
126
  // Handle name change - might need to rename file
102
127
  let newFilename = filename;
103
128
  let newFilePath = filePath;
@@ -3,6 +3,24 @@ import { promises as fs } from 'fs';
3
3
  import path from 'path';
4
4
  import matter from 'gray-matter';
5
5
  const app = new Hono();
6
+ function normalizeTools(value) {
7
+ if (!value)
8
+ return undefined;
9
+ if (Array.isArray(value)) {
10
+ const cleaned = value
11
+ .map((item) => (typeof item === 'string' ? item.trim() : ''))
12
+ .filter((item) => Boolean(item));
13
+ return cleaned.length > 0 ? Array.from(new Set(cleaned)) : undefined;
14
+ }
15
+ if (typeof value === 'string') {
16
+ const cleaned = value
17
+ .split(',')
18
+ .map((item) => item.trim())
19
+ .filter(Boolean);
20
+ return cleaned.length > 0 ? Array.from(new Set(cleaned)) : undefined;
21
+ }
22
+ return undefined;
23
+ }
6
24
  function getAgentsPath() {
7
25
  let basePath;
8
26
  if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
@@ -52,11 +70,11 @@ app.get('/', async (c) => {
52
70
  return {
53
71
  filename: file,
54
72
  metadata: {
73
+ ...data,
55
74
  name: data.name || title,
56
75
  description: data.description || '',
57
76
  color: data.color,
58
- tools: data.tools,
59
- ...data
77
+ tools: normalizeTools(data.tools)
60
78
  },
61
79
  content,
62
80
  title
@@ -115,13 +133,22 @@ app.post('/', async (c) => {
115
133
  // File doesn't exist, which is what we want
116
134
  }
117
135
  // Prepare frontmatter - filter out undefined values
118
- const frontmatter = Object.fromEntries(Object.entries({
136
+ const normalizedTools = normalizeTools(body.metadata?.tools);
137
+ const frontmatterSource = {
138
+ ...body.metadata,
119
139
  name: body.name,
120
- description: body.description,
121
- color: body.metadata?.color || 'blue',
122
- tools: body.metadata?.tools,
123
- ...body.metadata
124
- }).filter(([_, value]) => value !== undefined));
140
+ description: body.description
141
+ };
142
+ frontmatterSource.color = typeof body.metadata?.color === 'string' && body.metadata.color
143
+ ? body.metadata.color
144
+ : 'blue';
145
+ if (normalizedTools) {
146
+ frontmatterSource.tools = normalizedTools;
147
+ }
148
+ else {
149
+ delete frontmatterSource.tools;
150
+ }
151
+ const frontmatter = Object.fromEntries(Object.entries(frontmatterSource).filter(([, value]) => value !== undefined));
125
152
  // Create the markdown content with frontmatter
126
153
  const fileContent = matter.stringify(body.content || '', frontmatter);
127
154
  await fs.writeFile(filePath, fileContent, 'utf-8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovelybunch/api",
3
- "version": "1.0.59",
3
+ "version": "1.0.61",
4
4
  "type": "module",
5
5
  "main": "dist/server-with-static.js",
6
6
  "exports": {
@@ -32,9 +32,9 @@
32
32
  "dependencies": {
33
33
  "@hono/node-server": "^1.13.7",
34
34
  "@hono/node-ws": "^1.0.6",
35
- "@lovelybunch/core": "^1.0.59",
36
- "@lovelybunch/mcp": "^1.0.59",
37
- "@lovelybunch/types": "^1.0.59",
35
+ "@lovelybunch/core": "^1.0.61",
36
+ "@lovelybunch/mcp": "^1.0.61",
37
+ "@lovelybunch/types": "^1.0.61",
38
38
  "arctic": "^1.9.2",
39
39
  "bcrypt": "^5.1.1",
40
40
  "cookie": "^0.6.0",