@hesed/conni 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +130 -57
  2. package/dist/commands/conni/auth/add.d.ts +1 -0
  3. package/dist/commands/conni/auth/add.js +24 -18
  4. package/dist/commands/conni/auth/list.d.ts +20 -0
  5. package/dist/commands/conni/auth/list.js +36 -0
  6. package/dist/commands/conni/auth/profile.d.ts +11 -0
  7. package/dist/commands/conni/auth/profile.js +23 -0
  8. package/dist/commands/conni/auth/test.d.ts +3 -1
  9. package/dist/commands/conni/auth/test.js +10 -4
  10. package/dist/commands/conni/auth/update.d.ts +1 -0
  11. package/dist/commands/conni/auth/update.js +22 -23
  12. package/dist/commands/conni/content/attachment-download.d.ts +1 -0
  13. package/dist/commands/conni/content/attachment-download.js +2 -1
  14. package/dist/commands/conni/content/attachment.d.ts +1 -0
  15. package/dist/commands/conni/content/attachment.js +2 -1
  16. package/dist/commands/conni/content/comment-delete.d.ts +1 -0
  17. package/dist/commands/conni/content/comment-delete.js +2 -1
  18. package/dist/commands/conni/content/comment-update.d.ts +1 -0
  19. package/dist/commands/conni/content/comment-update.js +2 -1
  20. package/dist/commands/conni/content/comment.d.ts +1 -0
  21. package/dist/commands/conni/content/comment.js +2 -1
  22. package/dist/commands/conni/content/create.d.ts +2 -0
  23. package/dist/commands/conni/content/create.js +19 -4
  24. package/dist/commands/conni/content/delete.d.ts +1 -0
  25. package/dist/commands/conni/content/delete.js +2 -1
  26. package/dist/commands/conni/content/get.d.ts +1 -0
  27. package/dist/commands/conni/content/get.js +2 -1
  28. package/dist/commands/conni/content/search.d.ts +1 -0
  29. package/dist/commands/conni/content/search.js +2 -1
  30. package/dist/commands/conni/content/update.d.ts +1 -0
  31. package/dist/commands/conni/content/update.js +2 -1
  32. package/dist/commands/conni/space/get.d.ts +1 -0
  33. package/dist/commands/conni/space/get.js +2 -1
  34. package/dist/commands/conni/space/list.d.ts +1 -0
  35. package/dist/commands/conni/space/list.js +2 -1
  36. package/dist/config.d.ts +6 -2
  37. package/dist/config.js +68 -3
  38. package/dist/conni/conni-api.d.ts +5 -1
  39. package/dist/conni/conni-api.js +27 -3
  40. package/oclif.manifest.json +305 -84
  41. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { Command } from '@oclif/core';
1
+ import { Command, Flags } from '@oclif/core';
2
2
  import { action } from '@oclif/core/ux';
3
3
  import { readConfig } from '../../../config.js';
4
4
  import { clearClients, testConnection } from '../../../conni/conni-client.js';
@@ -6,10 +6,16 @@ export default class AuthTest extends Command {
6
6
  static args = {};
7
7
  static description = 'Test authentication and connection';
8
8
  static enableJsonFlag = true;
9
- static examples = ['<%= config.bin %> <%= command.id %>'];
10
- static flags = {};
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %>',
11
+ '<%= config.bin %> <%= command.id %> --profile work',
12
+ ];
13
+ static flags = {
14
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
15
+ };
11
16
  async run() {
12
- const config = await readConfig(this.config.configDir, this.log.bind(this));
17
+ const { flags } = await this.parse(AuthTest);
18
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
13
19
  if (!config) {
14
20
  return {
15
21
  error: 'Missing authentication config',
@@ -7,6 +7,7 @@ export default class AuthUpdate extends Command {
7
7
  static examples: string[];
8
8
  static flags: {
9
9
  email: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  };
@@ -6,11 +6,15 @@ import { default as path } from 'node:path';
6
6
  import { clearClients, testConnection } from '../../../conni/conni-client.js';
7
7
  export default class AuthUpdate extends Command {
8
8
  static args = {};
9
- static description = 'Update existing authentication';
9
+ static description = 'Update existing authentication profile';
10
10
  static enableJsonFlag = true;
11
- static examples = ['<%= config.bin %> <%= command.id %>'];
11
+ static examples = [
12
+ '<%= config.bin %> <%= command.id %>',
13
+ '<%= config.bin %> <%= command.id %> --profile work',
14
+ ];
12
15
  static flags = {
13
16
  email: Flags.string({ char: 'e', description: 'Account email', required: !process.stdout.isTTY }),
17
+ profile: Flags.string({ char: 'p', description: 'Profile name to update (default: "default")', required: false }),
14
18
  token: Flags.string({ char: 't', description: 'API Token', required: !process.stdout.isTTY }),
15
19
  url: Flags.string({
16
20
  char: 'u',
@@ -20,10 +24,11 @@ export default class AuthUpdate extends Command {
20
24
  };
21
25
  async run() {
22
26
  const { flags } = await this.parse(AuthUpdate);
23
- const configPath = path.join(this.config.configDir, 'conni-config.json');
24
- let config;
27
+ const profileName = flags.profile ?? 'default';
28
+ const configFilePath = path.join(this.config.configDir, 'conni-config.json');
29
+ let existing;
25
30
  try {
26
- config = await fs.readJSON(configPath);
31
+ existing = await fs.readJSON(configFilePath);
27
32
  }
28
33
  catch (error) {
29
34
  const msg = error instanceof Error ? error.message : String(error);
@@ -35,13 +40,15 @@ export default class AuthUpdate extends Command {
35
40
  }
36
41
  return;
37
42
  }
38
- const apiToken = flags.token ??
39
- (await input({ default: config.auth.apiToken, message: 'API Token:', prefill: 'tab', required: true }));
40
- const email = flags.email ??
41
- (await input({ default: config.auth.email, message: 'Account email:', prefill: 'tab', required: true }));
43
+ // Migrate legacy {auth: {...}} into profiles.default, then read current profile
44
+ const legacyAuth = existing.auth;
45
+ const profiles = (existing.profiles ?? (legacyAuth ? { default: legacyAuth } : {}));
46
+ const current = profiles[profileName] ?? {};
47
+ const apiToken = flags.token ?? (await input({ default: current.apiToken, message: 'API Token:', prefill: 'tab', required: true }));
48
+ const email = flags.email ?? (await input({ default: current.email, message: 'Account email:', prefill: 'tab', required: true }));
42
49
  const host = flags.url ??
43
50
  (await input({
44
- default: config.auth.host,
51
+ default: current.host,
45
52
  message: 'Atlassian instance URL (start with https://):',
46
53
  prefill: 'tab',
47
54
  required: true,
@@ -50,23 +57,15 @@ export default class AuthUpdate extends Command {
50
57
  if (!answer) {
51
58
  return;
52
59
  }
53
- const auth = {
54
- auth: {
55
- apiToken,
56
- email,
57
- host,
58
- },
59
- };
60
- await fs.writeJSON(configPath, auth, {
61
- mode: 0o600, // owner read/write only
62
- });
60
+ const updatedConfig = { ...existing, profiles: { ...profiles, [profileName]: { apiToken, email, host } } };
61
+ await fs.writeJSON(configFilePath, updatedConfig, { mode: 0o600 });
63
62
  action.start('Authenticating');
64
- config = await fs.readJSON(configPath);
65
- const result = await testConnection(config.auth);
63
+ const result = await testConnection({ apiToken, email, host });
66
64
  clearClients();
67
65
  if (result.success) {
68
66
  action.stop('✓ successful');
69
- this.log('Authentication updated successfully');
67
+ const profileSuffix = profileName === 'default' ? '' : ` for profile '${profileName}'`;
68
+ this.log(`Authentication${profileSuffix} updated successfully`);
70
69
  }
71
70
  else {
72
71
  action.stop('✗ failed');
@@ -7,6 +7,7 @@ export default class ContentDownloadAttachment extends Command {
7
7
  static description: string;
8
8
  static examples: string[];
9
9
  static flags: {
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
13
  run(): Promise<void>;
@@ -14,11 +14,12 @@ export default class ContentDownloadAttachment extends Command {
14
14
  '<%= config.bin %> <%= command.id %> att12345 ./document.pdf',
15
15
  ];
16
16
  static flags = {
17
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
17
18
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
18
19
  };
19
20
  async run() {
20
21
  const { args, flags } = await this.parse(ContentDownloadAttachment);
21
- const config = await readConfig(this.config.configDir, this.log.bind(this));
22
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
22
23
  if (!config) {
23
24
  return;
24
25
  }
@@ -7,6 +7,7 @@ export default class ContentAttachment extends Command {
7
7
  static description: string;
8
8
  static examples: string[];
9
9
  static flags: {
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
13
  run(): Promise<void>;
@@ -13,11 +13,12 @@ export default class ContentAttachment extends Command {
13
13
  static description = 'Add attachment to Confluence content';
14
14
  static examples = ['<%= config.bin %> <%= command.id %> 123456 ./document.pdf'];
15
15
  static flags = {
16
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
16
17
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
17
18
  };
18
19
  async run() {
19
20
  const { args, flags } = await this.parse(ContentAttachment);
20
- const config = await readConfig(this.config.configDir, this.log.bind(this));
21
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
21
22
  if (!config) {
22
23
  return;
23
24
  }
@@ -6,6 +6,7 @@ export default class ContentDeleteComment extends Command {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
11
12
  run(): Promise<void>;
@@ -9,11 +9,12 @@ export default class ContentDeleteComment extends Command {
9
9
  static description = 'Delete comment from Confluence content';
10
10
  static examples = ['<%= config.bin %> <%= command.id %> 1544224770'];
11
11
  static flags = {
12
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
12
13
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
13
14
  };
14
15
  async run() {
15
16
  const { args, flags } = await this.parse(ContentDeleteComment);
16
- const config = await readConfig(this.config.configDir, this.log.bind(this));
17
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
17
18
  if (!config) {
18
19
  return;
19
20
  }
@@ -7,6 +7,7 @@ export default class ContentUpdateComment extends Command {
7
7
  static description: string;
8
8
  static examples: string[];
9
9
  static flags: {
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
13
  run(): Promise<void>;
@@ -15,11 +15,12 @@ export default class ContentUpdateComment extends Command {
15
15
  '<%= config.bin %> <%= command.id %> 1544224770 "$(cat content.md)"',
16
16
  ];
17
17
  static flags = {
18
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
18
19
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
19
20
  };
20
21
  async run() {
21
22
  const { args, flags } = await this.parse(ContentUpdateComment);
22
- const config = await readConfig(this.config.configDir, this.log.bind(this));
23
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
23
24
  if (!config) {
24
25
  return;
25
26
  }
@@ -7,6 +7,7 @@ export default class ContentAddComment extends Command {
7
7
  static description: string;
8
8
  static examples: string[];
9
9
  static flags: {
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
13
  run(): Promise<void>;
@@ -15,11 +15,12 @@ export default class ContentAddComment extends Command {
15
15
  '<%= config.bin %> <%= command.id %> 123456 "$(cat content.md)"',
16
16
  ];
17
17
  static flags = {
18
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
18
19
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
19
20
  };
20
21
  async run() {
21
22
  const { args, flags } = await this.parse(ContentAddComment);
22
- const config = await readConfig(this.config.configDir, this.log.bind(this));
23
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
23
24
  if (!config) {
24
25
  return;
25
26
  }
@@ -6,6 +6,8 @@ export default class ContentCreate extends Command {
6
6
  static flags: {
7
7
  attach: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  fields: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
9
+ 'full-width': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
11
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
12
  };
11
13
  run(): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
+ import fs from 'fs-extra';
2
3
  import { readConfig } from '../../../config.js';
3
4
  import { clearClients, createPage, createPageWithMedia } from '../../../conni/conni-client.js';
4
5
  import { formatAsToon } from '../../../format.js';
@@ -11,6 +12,7 @@ export default class ContentCreate extends Command {
11
12
  '<%= config.bin %> <%= command.id %> --fields spaceKey="DEV" title="Child page" body="Content" parentId="123456"',
12
13
  '<%= config.bin %> <%= command.id %> --fields spaceKey="DEV" title="Page with image" body="See the diagram:\n![diagram](./diagram.png)" --attach ./diagram.png',
13
14
  '<%= config.bin %> <%= command.id %> --fields spaceKey="DEV" title="Page with files" body="Content" --attach ./image.png --attach ./report.pdf',
15
+ '<%= config.bin %> <%= command.id %> --fields spaceKey="DEV" title="Storage page" body=@storage.xml representation=storage --full-width',
14
16
  ];
15
17
  static flags = {
16
18
  attach: Flags.string({
@@ -19,16 +21,21 @@ export default class ContentCreate extends Command {
19
21
  required: false,
20
22
  }),
21
23
  fields: Flags.string({
22
- description: 'Minimum fields required: spaceKey, title & body',
24
+ description: 'Content fields in key=value format. Use @file to read value from a file (e.g. body=@content.xml)',
23
25
  multiple: true,
24
26
  required: true,
25
- summary: 'Content fields in key=value format',
27
+ summary: 'Minimum fields required: spaceKey, title & body',
26
28
  }),
29
+ 'full-width': Flags.boolean({
30
+ description: 'Set page appearance to full-width',
31
+ required: false,
32
+ }),
33
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
27
34
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
28
35
  };
29
36
  async run() {
30
37
  const { flags } = await this.parse(ContentCreate);
31
- const config = await readConfig(this.config.configDir, this.log.bind(this));
38
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
32
39
  if (!config) {
33
40
  return;
34
41
  }
@@ -36,7 +43,12 @@ export default class ContentCreate extends Command {
36
43
  if (flags.fields) {
37
44
  for (const field of flags.fields) {
38
45
  const [key, ...valueParts] = field.split('=');
39
- const value = valueParts.join('=');
46
+ let value = valueParts.join('=');
47
+ // Support @file syntax to read value from a file
48
+ if (value.startsWith('@')) {
49
+ const filePath = value.slice(1);
50
+ value = fs.readFileSync(filePath, 'utf8');
51
+ }
40
52
  fields[key] = value;
41
53
  }
42
54
  }
@@ -46,6 +58,9 @@ export default class ContentCreate extends Command {
46
58
  this.error(`Required field "${required}" is missing`);
47
59
  }
48
60
  }
61
+ if (flags['full-width']) {
62
+ fields.fullWidth = 'true';
63
+ }
49
64
  const result = flags.attach
50
65
  ? await createPageWithMedia(config.auth, fields, flags.attach)
51
66
  : await createPage(config.auth, fields);
@@ -6,6 +6,7 @@ export default class ContentDelete extends Command {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
11
12
  run(): Promise<void>;
@@ -9,11 +9,12 @@ export default class ContentDelete extends Command {
9
9
  static description = 'Delete a Confluence page';
10
10
  static examples = ['<%= config.bin %> <%= command.id %> 1543634992'];
11
11
  static flags = {
12
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
12
13
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
13
14
  };
14
15
  async run() {
15
16
  const { args, flags } = await this.parse(ContentDelete);
16
- const config = await readConfig(this.config.configDir, this.log.bind(this));
17
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
17
18
  if (!config) {
18
19
  return;
19
20
  }
@@ -6,6 +6,7 @@ export default class ContentGet extends Command {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
11
12
  run(): Promise<void>;
@@ -9,11 +9,12 @@ export default class ContentGet extends Command {
9
9
  static description = 'Get details of a Confluence content';
10
10
  static examples = ['<%= config.bin %> <%= command.id %> 1544060948'];
11
11
  static flags = {
12
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
12
13
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
13
14
  };
14
15
  async run() {
15
16
  const { args, flags } = await this.parse(ContentGet);
16
- const config = await readConfig(this.config.configDir, this.log.bind(this));
17
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
17
18
  if (!config) {
18
19
  return;
19
20
  }
@@ -8,6 +8,7 @@ export default class ContentSearch extends Command {
8
8
  static flags: {
9
9
  expand: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
10
  limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  };
13
14
  run(): Promise<void>;
@@ -14,11 +14,12 @@ export default class ContentSearch extends Command {
14
14
  static flags = {
15
15
  expand: Flags.string({ description: 'Properties of the content to expand', required: false }),
16
16
  limit: Flags.integer({ description: 'Maximum number of contents per page', required: false }),
17
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
17
18
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
18
19
  };
19
20
  async run() {
20
21
  const { args, flags } = await this.parse(ContentSearch);
21
- const config = await readConfig(this.config.configDir, this.log.bind(this));
22
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
22
23
  if (!config) {
23
24
  return;
24
25
  }
@@ -7,6 +7,7 @@ export default class ContentUpdate extends Command {
7
7
  static examples: string[];
8
8
  static flags: {
9
9
  fields: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
10
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  };
11
12
  run(): Promise<void>;
12
13
  }
@@ -13,10 +13,11 @@ export default class ContentUpdate extends Command {
13
13
  ];
14
14
  static flags = {
15
15
  fields: Flags.string({ description: 'Content fields to update in key=value format', multiple: true, required: true }),
16
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
16
17
  };
17
18
  async run() {
18
19
  const { args, flags } = await this.parse(ContentUpdate);
19
- const config = await readConfig(this.config.configDir, this.log.bind(this));
20
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
20
21
  if (!config) {
21
22
  return;
22
23
  }
@@ -6,6 +6,7 @@ export default class SpaceGet extends Command {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
11
12
  run(): Promise<void>;
@@ -9,11 +9,12 @@ export default class SpaceGet extends Command {
9
9
  static description = 'Get details of a Confluence space';
10
10
  static examples = ['<%= config.bin %> <%= command.id %> DEV'];
11
11
  static flags = {
12
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
12
13
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
13
14
  };
14
15
  async run() {
15
16
  const { args, flags } = await this.parse(SpaceGet);
16
- const config = await readConfig(this.config.configDir, this.log.bind(this));
17
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
17
18
  if (!config) {
18
19
  return;
19
20
  }
@@ -3,6 +3,7 @@ export default class SpaceList extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
7
  toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
8
  };
8
9
  run(): Promise<void>;
@@ -6,11 +6,12 @@ export default class SpaceList extends Command {
6
6
  static description = 'List all Confluence spaces';
7
7
  static examples = ['<%= config.bin %> <%= command.id %>'];
8
8
  static flags = {
9
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
9
10
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
10
11
  };
11
12
  async run() {
12
13
  const { flags } = await this.parse(SpaceList);
13
- const config = await readConfig(this.config.configDir, this.log.bind(this));
14
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
14
15
  if (!config) {
15
16
  return;
16
17
  }
package/dist/config.d.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  interface AuthConfig {
2
2
  apiToken: string;
3
- email: string;
3
+ email?: string;
4
4
  host: string;
5
5
  }
6
6
  interface Config {
7
7
  auth: AuthConfig;
8
8
  }
9
- export declare function readConfig(configDir: string, log: (message: string) => void): Promise<Config | undefined>;
9
+ export type Profiles = Record<string, AuthConfig>;
10
+ export declare function getDefaultProfile(configDir: string): Promise<string>;
11
+ export declare function setDefaultProfile(configDir: string, profile: string, log: (message: string) => void): Promise<void>;
12
+ export declare function readConfig(configDir: string, log: (message: string) => void, profile?: string): Promise<Config | undefined>;
13
+ export declare function readProfiles(configDir: string, log: (message: string) => void): Promise<Profiles | undefined>;
10
14
  export {};
package/dist/config.js CHANGED
@@ -1,9 +1,50 @@
1
1
  import { default as fs } from 'fs-extra';
2
2
  import { default as path } from 'node:path';
3
- export async function readConfig(configDir, log) {
4
- const configPath = path.join(configDir, 'conni-config.json');
3
+ const CONFIG_FILE = 'conni-config.json';
4
+ function configPath(configDir) {
5
+ return path.join(configDir, CONFIG_FILE);
6
+ }
7
+ export async function getDefaultProfile(configDir) {
8
+ const cp = configPath(configDir);
9
+ try {
10
+ const raw = await fs.readJSON(cp);
11
+ return raw.defaultProfile || 'default';
12
+ }
13
+ catch {
14
+ return 'default';
15
+ }
16
+ }
17
+ export async function setDefaultProfile(configDir, profile, log) {
18
+ const profiles = await readProfiles(configDir, log);
19
+ if (!profiles || !(profile in profiles)) {
20
+ log(`Profile '${profile}' not found`);
21
+ return;
22
+ }
23
+ const cp = configPath(configDir);
24
+ const raw = await fs.readJSON(cp);
25
+ raw.defaultProfile = profile;
26
+ await fs.writeJSON(cp, raw, { spaces: 2 });
27
+ log(`Default profile set to '${profile}'`);
28
+ }
29
+ export async function readConfig(configDir, log, profile) {
30
+ const cp = configPath(configDir);
5
31
  try {
6
- return await fs.readJSON(configPath);
32
+ const raw = await fs.readJSON(cp);
33
+ if (raw.profiles) {
34
+ const resolvedProfile = profile ?? (await getDefaultProfile(configDir));
35
+ const auth = raw.profiles[resolvedProfile];
36
+ if (!auth) {
37
+ log(`Profile '${resolvedProfile}' not found`);
38
+ return undefined;
39
+ }
40
+ return { auth };
41
+ }
42
+ // backward compat: old { auth: {...} } format
43
+ if (profile && profile !== 'default') {
44
+ log(`Profile '${profile}' not found`);
45
+ return undefined;
46
+ }
47
+ return raw;
7
48
  }
8
49
  catch (error) {
9
50
  const msg = error instanceof Error ? error.message : String(error);
@@ -16,3 +57,27 @@ export async function readConfig(configDir, log) {
16
57
  return undefined;
17
58
  }
18
59
  }
60
+ export async function readProfiles(configDir, log) {
61
+ const cp = configPath(configDir);
62
+ try {
63
+ const raw = await fs.readJSON(cp);
64
+ if (raw.profiles) {
65
+ return raw.profiles;
66
+ }
67
+ // backward compat: treat old { auth: {...} } as the default profile
68
+ if (raw.auth) {
69
+ return { default: raw.auth };
70
+ }
71
+ return {};
72
+ }
73
+ catch (error) {
74
+ const msg = error instanceof Error ? error.message : String(error);
75
+ if (msg.toLowerCase().includes('no such file or directory')) {
76
+ log('No authentication profiles found');
77
+ }
78
+ else {
79
+ log(msg);
80
+ }
81
+ return undefined;
82
+ }
83
+ }
@@ -9,7 +9,7 @@ export interface ApiResult {
9
9
  }
10
10
  export interface Config {
11
11
  apiToken: string;
12
- email: string;
12
+ email?: string;
13
13
  host: string;
14
14
  }
15
15
  /**
@@ -74,6 +74,10 @@ export declare class ConniApi {
74
74
  * Search pages using CQL
75
75
  */
76
76
  searchContents(cql: string, limit?: number, expand?: string[]): Promise<ApiResult>;
77
+ /**
78
+ * Set page appearance (full-width or fixed-width)
79
+ */
80
+ setPageAppearance(pageId: string, appearance: string): Promise<ApiResult>;
77
81
  /**
78
82
  * Test Confluence API connection
79
83
  */