@hesed/conni 0.5.0 → 0.6.1

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 +131 -56
  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 +1 -0
  23. package/dist/commands/conni/content/create.js +2 -1
  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 +3 -0
  31. package/dist/commands/conni/content/update.js +29 -4
  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 +1 -1
  39. package/dist/conni/conni-api.js +11 -4
  40. package/oclif.manifest.json +235 -7
  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
  }
@@ -7,6 +7,7 @@ export default class ContentCreate extends Command {
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
9
  'full-width': import("@oclif/core/interfaces").BooleanFlag<boolean>;
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>;
@@ -30,11 +30,12 @@ export default class ContentCreate extends Command {
30
30
  description: 'Set page appearance to full-width',
31
31
  required: false,
32
32
  }),
33
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
33
34
  toon: Flags.boolean({ description: 'Format output as toon', required: false }),
34
35
  };
35
36
  async run() {
36
37
  const { flags } = await this.parse(ContentCreate);
37
- 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);
38
39
  if (!config) {
39
40
  return;
40
41
  }
@@ -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,9 @@ 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
+ 'full-width': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
13
  };
11
14
  run(): Promise<void>;
12
15
  }
@@ -1,6 +1,8 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
+ import fs from 'fs-extra';
2
3
  import { readConfig } from '../../../config.js';
3
4
  import { clearClients, updateContent } from '../../../conni/conni-client.js';
5
+ import { formatAsToon } from '../../../format.js';
4
6
  export default class ContentUpdate extends Command {
5
7
  static args = {
6
8
  pageId: Args.string({ description: 'Page ID', required: true }),
@@ -10,13 +12,24 @@ export default class ContentUpdate extends Command {
10
12
  "<%= config.bin %> <%= command.id %> 1076199489 --fields title='New summary' body='New description'",
11
13
  "<%= config.bin %> <%= command.id %> 1076199489 --fields body='\n# Header\n## Sub-header\n- Item 1\n- Item 2\n```bash\nls -a\n```'",
12
14
  '<%= config.bin %> <%= command.id %> 1076199489 --fields body="$(cat content.md)"',
15
+ '<%= config.bin %> <%= command.id %> 1076199489 --fields body=@storage.xml representation=storage --full-width',
13
16
  ];
14
17
  static flags = {
15
- fields: Flags.string({ description: 'Content fields to update in key=value format', multiple: true, required: true }),
18
+ fields: Flags.string({
19
+ description: 'Content fields to update in key=value format. Use @file to read value from a file (e.g. body=@content.xml)',
20
+ multiple: true,
21
+ required: true,
22
+ }),
23
+ 'full-width': Flags.boolean({
24
+ description: 'Set page appearance to full-width',
25
+ required: false,
26
+ }),
27
+ profile: Flags.string({ char: 'p', description: 'Authentication profile name', required: false }),
28
+ toon: Flags.boolean({ description: 'Format output as toon', required: false }),
16
29
  };
17
30
  async run() {
18
31
  const { args, flags } = await this.parse(ContentUpdate);
19
- const config = await readConfig(this.config.configDir, this.log.bind(this));
32
+ const config = await readConfig(this.config.configDir, this.log.bind(this), flags.profile);
20
33
  if (!config) {
21
34
  return;
22
35
  }
@@ -24,12 +37,24 @@ export default class ContentUpdate extends Command {
24
37
  if (flags.fields) {
25
38
  for (const field of flags.fields) {
26
39
  const [key, ...valueParts] = field.split('=');
27
- const value = valueParts.join('=');
40
+ let value = valueParts.join('=');
41
+ if (value.startsWith('@')) {
42
+ const filePath = value.slice(1);
43
+ value = fs.readFileSync(filePath, 'utf8');
44
+ }
28
45
  fields[key] = value;
29
46
  }
30
47
  }
48
+ if (flags['full-width']) {
49
+ fields.fullWidth = 'true';
50
+ }
31
51
  const result = await updateContent(config.auth, args.pageId, fields);
32
52
  clearClients();
33
- this.logJson(result);
53
+ if (flags.toon) {
54
+ this.log(formatAsToon(result));
55
+ }
56
+ else {
57
+ this.logJson(result);
58
+ }
34
59
  }
35
60
  }
@@ -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
  /**
@@ -271,7 +271,7 @@ export class ConniApi {
271
271
  authentication: {
272
272
  basic: {
273
273
  apiToken: this.config.apiToken,
274
- email: this.config.email,
274
+ email: this.config.email ?? '',
275
275
  },
276
276
  },
277
277
  host: this.config.host,
@@ -465,6 +465,8 @@ export class ConniApi {
465
465
  const currentVersion = (page.version?.number ?? 0) + 1;
466
466
  const title = fields.title ?? page.title ?? '';
467
467
  const body = fields.body;
468
+ const representation = fields.representation ?? 'atlas_doc_format';
469
+ const isStorage = representation === 'storage';
468
470
  const response = await client.content.updateContent({
469
471
  id: pageId,
470
472
  type: 'page',
@@ -473,9 +475,11 @@ export class ConniApi {
473
475
  : {
474
476
  body: {
475
477
  storage: {
476
- representation: 'atlas_doc_format',
477
- // eslint-disable-next-line unicorn/prefer-string-replace-all
478
- value: JSON.stringify(markdownToAdf(body.replace(/\\n/g, '\n'))),
478
+ representation,
479
+ value: isStorage
480
+ ? body
481
+ : // eslint-disable-next-line unicorn/prefer-string-replace-all
482
+ JSON.stringify(markdownToAdf(body.replace(/\\n/g, '\n'))),
479
483
  },
480
484
  },
481
485
  }),
@@ -484,6 +488,9 @@ export class ConniApi {
484
488
  number: currentVersion,
485
489
  },
486
490
  });
491
+ if (fields.fullWidth) {
492
+ await this.setPageAppearance(pageId, 'full-width');
493
+ }
487
494
  return {
488
495
  data: response,
489
496
  success: true,